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. GetVoiceBot-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 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.

Understand the helper contract

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

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/.

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:

import json
from urllib import error, parse, request

from gvb_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 gvb_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.


For executable request templates and error-handling rules, use the GetVoiceBot API Skill.