> ## Documentation Index
> Fetch the complete documentation index at: https://vida.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspaces, helpers, and browser automation

> Manage Computer Agent files and create exact reusable Browser or Browser-independent functions.

Workspace APIs manage customer-authored project files, skill source, reference material, and automation artifacts. Vida-managed Agent configuration remains in the staging and publish flow and is not ordinary workspace content.

## Use one workspace path model

All workspace paths are relative to the selected Agent workspace.

* Read text with `POST /workspace/read`.
* Browse with `/workspace/list`, `/workspace/find`, and `/workspace/search`.
* Write or edit text with `/workspace/write` and `/workspace/edit`.
* Preview or download a file with `GET /workspace/preview` or `/workspace/download`.
* Upload with multipart `POST /workspace/upload`.
* Delete one file or empty directory with `DELETE /workspace/delete?path=...`.

Read before mutation and verify afterward. Uploads do not overwrite unless explicitly requested. Delete directory contents individually; recursive deletion is not supported.

`workspace/write` always accepts complete text. To write a JSON file, pass the serialized document
as the request body's `content` string; do not pass the document as a nested object. Zero-byte files
are supported through multipart upload.

Canvas source is also workspace content. Address its `app` directory directly even though it does
not appear in a root listing, and use the Canvas publish action rather than editing generated
`public` files. See [Computer Agent Canvas](/docs/api-reference/agent-guides/computer-agent-canvas).

## Understand the helper contract

`GET /helpers` lists exact callable functions for the selected Computer Agent. Each entry includes:

* registered name
* argument schema
* whether Browser access is required
* owning skill slug
* declared managed-credential IDs

Execute one with `POST /helpers/execute` and `{name, arguments}`. Never send credential values as arguments. Check both the transport response and the helper's returned status and business result.

## Author a reusable helper

Reusable source belongs with its owning skill under `skills/{skillSlug}/helpers/`.

* Use `@browser_function(...)` when the operation requires the Agent's Browser session.
* Use `@computer_function(...)` when the operation can run without opening or consulting the Browser.
* Declare required credential IDs in source and resolve them at execution time.
* Keep the helper contract narrow, independently invocable, and based on durable inputs rather than hidden prior state.

Skill install, update, and uninstall refresh helper registration. When source is added or changed
through the workspace API, call `POST /helpers/refresh` before listing or executing the helper.

### Example: call an API without opening the Browser

Create an owning skill such as `skills/customer-records/SKILL.md`, then write this helper to
`skills/customer-records/helpers/lookup_customer.py`:

```python theme={null}
import json
from urllib import error, parse, request

from vida_helper_runtime import computer_function, managed_secret


API_ROOT = "https://api.example.com/v1"


@computer_function(
    domain="api.example.com",
    description="Look up one customer by its external ID.",
    args={
        "type": "object",
        "required": ["customer_id"],
        "properties": {
            "customer_id": {
                "type": "string",
                "description": "Exact customer ID from the source system.",
            },
        },
    },
    result={
        "type": "object",
        "required": ["status", "summary"],
        "properties": {
            "status": {"type": "string", "enum": ["done", "blocked", "need_user"]},
            "summary": {"type": "string"},
            "reason": {"type": "string"},
            "httpStatus": {"type": "integer"},
            "result": {
                "type": "object",
                "properties": {
                    "customerId": {"type": "string"},
                    "name": {"type": "string"},
                },
            },
        },
    },
    required_secrets=["example_api_token"],
)
def lookup_customer(customer_id):
    customer_id = str(customer_id or "").strip()
    if not customer_id:
        return {
            "status": "blocked",
            "summary": "A customer ID is required.",
            "reason": "invalid_customer_id",
        }

    token = managed_secret("example_api_token")
    url = f"{API_ROOT}/customers/{parse.quote(customer_id, safe='')}"
    api_request = request.Request(
        url,
        headers={"Authorization": f"Bearer {token}", "Accept": "application/json"},
    )

    try:
        with request.urlopen(api_request, timeout=15) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except error.HTTPError as exc:
        if exc.code in (401, 403):
            return {
                "status": "need_user",
                "summary": "The API connection must be updated.",
                "reason": "authentication_required",
                "httpStatus": exc.code,
            }
        return {
            "status": "blocked",
            "summary": "The customer API rejected the lookup.",
            "reason": "api_error",
            "httpStatus": exc.code,
        }
    except (error.URLError, TimeoutError, json.JSONDecodeError):
        return {
            "status": "blocked",
            "summary": "The customer API could not return a usable response.",
            "reason": "api_unavailable",
        }

    return {
        "status": "done",
        "summary": "Customer found.",
        "result": {
            "customerId": str(payload.get("id", customer_id)),
            "name": str(payload.get("name", "")),
        },
    }
```

The decorator metadata is the public callable contract. Keep its argument and result descriptions
literal and synchronized with what the function actually accepts and returns. The helper imports
`computer_function` and `managed_secret` from the stable `vida_helper_runtime`; never place a token
in source, workspace data, helper arguments, or a returned result.

Complete this workflow for the selected Computer Agent:

1. Write the owning `SKILL.md` and helper source with `POST /workspace/write`.
2. Save `example_api_token` with `POST /secrets` using `{secretId, value}`.
3. Call `POST /helpers/refresh`. Require `ok: true`, no validation errors, and the expected helper
   contract in `functions`.
4. Confirm the same contract through `GET /helpers`.
5. Run a safe lookup through `POST /helpers/execute` with
   `{name: "lookup_customer", arguments: {customer_id: "..."}}`.
6. Check the helper's structured status and business result. For a write operation, also read the
   destination system before retrying after a timeout or uncertain response.
7. Discover the Agent's available functions, stage the exact `computer` action and argument
   mapping, run a conversational test, then publish only after it succeeds.

Repeat refresh, list, and representative execution after every source change.

## Record and generate a Browser workflow

For an agent-controlled workflow:

1. Create `POST /browser/automation-sessions` and retain its `cdpUrl`, headers, slot, and expiration.
2. Read `{cdpUrl}/json/version` with the returned header and connect browser tooling to its `webSocketDebuggerUrl`.
   For local automation, Browser Harness is the recommended client: provide that ticket-bearing
   WebSocket URL as `BU_CDP_WS`. Do not provide the protected discovery URL as `BU_CDP_URL` when the
   client cannot attach the required discovery header.
3. Confirm no recording or generation is active.
4. Start recording with the automation session's exact slot.
5. Perform the representative workflow through the CDP connection.
6. Stop recording and retain `domainKey`, `recordingId`, and `evidenceRoot`. Require
   `finalized: true` and `analysisStatus: completed` before generation.
7. Start generation, optionally supplying a neutral `skillName`.
8. Retain the returned `generationId` and immutable `recordingIds` evidence snapshot. New recordings
   require a new generation; they are not appended to work already in progress.
9. Poll `phase`, `updatedAt`, and `lastActivityAt` until terminal and use the returned conversation
   reference to inspect work and tool calls.
10. Re-list helpers and test every required operation.

Use `POST /browser/ticket` instead when a person needs temporary interactive access, such as signing in. Open the returned `launchRef.href`; do not construct a different link.

## Expose a helper to a conversational Agent

A Computer Agent uses helpers on its own Computer automatically. Set `computerDelegateAccountId`
only when the helper belongs to a different Agent's Computer. Discover the function catalog and
configure the exact `computer` action. Its instructions must map conversation values to the helper's
exact argument names.

The `computer` function can call an exact helper or perform broader work. The `browser` function is
for ad hoc work in the live Browser, not registered helpers. Test staging with safe data before
publishing.

## Accept a reusable helper

Verify three separate boundaries before relying on a helper:

1. **Registry:** `/helpers` exposes its exact inputs, result metadata, owner, Browser requirement,
   and required credential IDs.
2. **Execution:** a safe representative `/helpers/execute` call works and its normalized runtime
   fields match the registered result metadata. Verify the destination effect when one is expected.
3. **Conversation:** Agent function discovery exposes Computer, and a staged conversational test can
   select and use the helper in the intended environment.

Preserve opaque IDs returned by helpers byte-for-byte. Do not infer that a numeric ID from an
application URL is interchangeable.

<Note>
  For executable request templates and error-handling rules, use the [Vida API Skill](/docs/api-reference/vida-api-skill).
</Note>
