Dory is in development. The invite-only beta has not opened yet; prices and limits are planned and may change.

Agents overview

Use an Agent Computer as an external tool, with the model client and its provider credentials on your own machine. The model can request command execution, file operations, screenshots and desktop actions. The guest's root access is powerful; it does not make text read from a web page or repository trustworthy instructions.

Pick the interface

Use the SDK for application code, the CLI for scripts, or MCP for a compatible client. The Claude adapter translates the computer-use protocol into desktop actions; the OpenAI adapter has an explicit safety-check callback. Neither adapter grants permissions beyond the API key. Give a separate restricted key to each job and retain its computer and operation IDs.

Human takeover

Pause the agent's action loop before requesting help. Create a control-mode desktop session with exclusive control, retain its session ID, and share only its returned viewer URL with the intended person. Creating a link does not establish a connected holder. Poll the original desktop-control endpoint until holder is human, session_id matches the retained ID, and exclusive is true.

While connected, ordinary desktop actions return human_in_control. A disconnect is not the person's completion signal. Wait for explicit completion or cancellation, delete the retained session, then read control again to verify that no exclusive human holder remains. Failed polling, timeout, deletion or readback leaves the action loop paused. Do not set force in this workflow. If the link expires before connection, create a replacement only after the person agrees or explicitly cancel the handoff.

Action Python TypeScript CLI MCP
Read holder computer.desktop.control() await computer.desktop.control() cec desktop control ID --json get_desktop_control
List metadata computer.desktop.sessions.list() await computer.desktop.sessions.list() cec desktop sessions ls ID --json Retain the creation ID
End session computer.desktop.sessions.delete(id) await computer.desktop.sessions.delete(id) cec desktop sessions rm ID SESSION end_desktop_session

Bound the job

Set a maximum number of model turns, a local deadline, a computer run timer and a cost limit. These stop different activities; a local client timeout does not cancel an accepted server operation. Prefer an egress allowlist and keep API/provider secrets out of the guest. Use view-only links for observation. See Agent keys, Always-on agents and Access tokens.

Complete handoff programs

These programs use an existing running computer named by CE_COMPUTER_ID. Keep CE_API_KEY in the private environment; the SDKs also accept CE_API_URL. They stop this agent's action loop, create one exclusive session, and wait up to 120 seconds for its connected holder. The session output contains credentials and belongs only in the intended viewer's private channel, never a report or model transcript.

After the connected record, the intended person supplies one JSON line on stdin with the retained session_id and a decision of complete or cancel. An expired link, a disconnect, absent input or a failed readback is not completion. Every program deletes its own session and checks control again before returning. These programs do not pause, delete or claim ownership of the supplied computer. The same sequence is available over raw HTTP, with the CLI (cec desktop) and with the MCP tools (desktop_link, get_desktop_control, end_desktop_session).

Python SDK handoff

import json
import os
import sys
import time
from computeengines import ApiError, Client

# Use a running computer you own. Keep this program's output private:
# the session record includes credentials for the intended viewer.
with Client() as client:
    computer = client.computers.get(os.environ["CE_COMPUTER_ID"])
    session = computer.desktop.viewer(mode="control", exclusive=True, expires_in=300)
    try:
        print(json.dumps({"kind": "session", "session": session.model_dump(mode="json")}), flush=True)
        deadline = time.monotonic() + 120
        while time.monotonic() < deadline:
            sessions = computer.desktop.sessions.list().data
            connected = any(s.id == session.id and s.connected is True for s in sessions)
            control = computer.desktop.control()
            if connected and control.holder == "human" and control.session_id == session.id and control.exclusive:
                break
            time.sleep
        else:
            raise RuntimeError("Viewer did not connect; agent work remains paused")
        try:
            computer.desktop.key("escape")  # ordinary action, never force=True
        except ApiError as error:
            if error.status != 409 or error.code != "human_in_control":
                raise
        else:
            raise RuntimeError("Expected the connected human to retain control")
        print(json.dumps({"kind": "connected", "session_id": session.id}), flush=True)
        # A disconnected socket is not completion. Only the intended human supplies this line.
        decision = json.loads(sys.stdin.readline(4097))
        if (set(decision) != {"session_id", "decision"} or decision["session_id"] != session.id
                or decision["decision"] not in {"complete", "cancel"}):
            raise RuntimeError("Explicit completion or cancellation is required")
    finally:
        computer.desktop.sessions.delete(session.id)
        if (any(s.id == session.id for s in computer.desktop.sessions.list().data)
                or computer.desktop.control().session_id == session.id):
            raise RuntimeError("Session revocation was not confirmed; remain paused")
    if decision["decision"] == "complete":
        computer.desktop.key("escape")
    print(json.dumps({"kind": "finished", "decision": decision["decision"]}))

TypeScript SDK handoff

import { createInterface } from "node:readline/promises";
import { Client, ApiError } from "@computeengines/agent-computers";

const id = process.env.CE_COMPUTER_ID;
if (!id) throw new Error("Set CE_COMPUTER_ID to a running computer you own");
const client = new Client();
try {
  const computer = await client.computers.get(id);
  const session = await computer.desktop.viewer({ mode: "control", exclusive: true, expires_in: 300 });
  let decision;
  try {
    // Private output: this actual session contains viewer credentials.
    console.log(JSON.stringify({ kind: "session", session }));
    const deadline = performance.now() + 120_000;
    let connected = false;
    while (performance.now() < deadline) {
      const sessions = await computer.desktop.sessions.list();
      const control = await computer.desktop.control();
      connected = sessions.data.some(s => s.id === session.id && s.connected === true)
        && control.holder === "human" && control.session_id === session.id && control.exclusive;
      if (connected) break;
      await new Promise(resolve => setTimeout(resolve, 500));
    }
    if (!connected) throw new Error("Viewer did not connect; remain paused");
    try {
      await computer.desktop.key("escape");
      throw new Error("Expected human control refusal");
    } catch (error) {
      if (!(error instanceof ApiError) || error.status !== 409 || error.code !== "human_in_control") throw error;
    }
    console.log(JSON.stringify({ kind: "connected", session_id: session.id }));
    const input = createInterface({ input: process.stdin, output: process.stderr });
    try {
      const line = await input.question("", { signal: AbortSignal.timeout(120_000) });
      if (line.length > 4096) throw new Error("Completion input too long");
      const value = JSON.parse(line);
      if (value === null || typeof value !== "object" || Object.keys(value).sort().join() !== "decision,session_id"
          || value.session_id !== session.id || !["complete", "cancel"].includes(value.decision)) {
        throw new Error("Explicit completion or cancellation is required");
      }
      decision = value.decision;
    } finally { input.close(); }
  } finally {
    await computer.desktop.sessions.delete(session.id);
    if ((await computer.desktop.sessions.list()).data.some(s => s.id === session.id)
        || (await computer.desktop.control()).session_id === session.id) throw new Error("Remain paused: revocation unconfirmed");
  }
  if (decision === "complete") await computer.desktop.key("escape");
  console.log(JSON.stringify({ kind: "finished", decision }));
} finally { client.close(); }

Dory is in development; these pages describe the planned service and may change. Questions: support@computeengines.com.