Connect a remote harness

Keep your agent loop and map its tools to remote session operations.

Agent skill

Your harness keeps model calls, conversation history, and tool selection. Assemble supplies the computer those tools use. Connect your tool adapter with an operator-issued product key and local SDK packages.

Complete the quickstart setup to build the SDK and configure your service URL and API key.

Adapt your environment tools

import { AssembleClient } from "@assemble-workspace/sdk";

const client = new AssembleClient({
  apiKey: process.env.ASSEMBLE_API_KEY ?? "",
  baseUrl: process.env.ASSEMBLE_BASE_URL ?? "",
});
const session = await client.sessions.open({ name: "my-agent-project" });

const environment = {
  read: async ({ path }) => new TextDecoder().decode(await session.files.read(path)),
  write: async ({ path, content }) => session.files.write(path, content),
  bash: async ({ command }, onOutput) => {
    const execution = await session.exec({ command });
    for await (const event of execution.events()) {
      if (event.type === "stdout" || event.type === "stderr") {
        await onOutput(event);
      }
    }
    return execution.wait();
  },
};

await environment.write({ path: "notes.txt", content: "Saved by my harness.\n" });
const result = await environment.bash({ command: "cat notes.txt" }, async (event) =>
  process.stdout.write(event.data),
);
console.log(result.state, result.exitCode);

Supply these functions as the implementations of your framework's tools. Its tool definitions and result format stay under your control. The repository also includes examples/external-harness.mjs.

Return actual execution state and exit code to the model. Bound the command text you include in model context; you can stream full output to your application without retaining every chunk in a string.

Reconnect without rerunning a command

Save the execution ID and the highest event sequence your application processed. After a transport failure, reconnect to that execution:

const execution = await session.execution(savedExecutionId);
for await (const event of execution.events({ after: lastSequence })) {
  lastSequence = event.sequence;
  // Apply each event and persist the new sequence in your application.
}
const result = await execution.wait();

The stream uses newline-delimited JSON. The SDK parses it and validates increasing sequence numbers. Reconnecting does not start another command.

Cancel explicitly

Aborting a request or closing execution.events() stops your connection. To stop the process, request cancellation and wait for a confirmed terminal state:

await execution.cancel();
const result = await execution.wait();
console.log(result.state);

If the service reports execution_unsettled, command completion is uncertain and the session stays reserved. Inspect the execution or involve the operator before repeating a side-effecting command.

Keep session identity stable

Reuse a session name or ID for later tasks. Store conversation state in your own application. Your file and command tools use the same writable session filesystem as processes inside the VM. The API retains command records; ordinary file writes have the flush and recovery behavior described in Persistence. Failed or cancelled commands can leave partial changes, and there is no automatic per-command rollback.

On this page