Skip to content

fix(sandbox): expose SDK-compatible TCloudSandbox client for full CreateSandboxOptions #44

@drewstone

Description

@drewstone

Problem

TCloudSandbox.create() currently accepts TCloudSandboxCreateOptions, maps that into a simplified Sandbox SDK create body, and returns { sandbox, attestation, ... }.

That is useful for CLI/simple sandbox creation, but it is not SDK-compatible with code that expects a SandboxClient shape:

interface SandboxClient {
  create(options?: CreateSandboxOptions): Promise<SandboxInstance>
}

@tangle-network/agent-runtime runLoop uses that shape and passes full CreateSandboxOptions, including fields like:

{
  backend: {
    type: 'opencode',
    profile: agentProfile,
    model: {
      provider: 'openai-compat',
      model: 'deepseek-v4-pro',
      baseUrl: 'https://router.tangle.tools/v1',
      apiKey: '...'
    }
  },
  env: { ... }
}

If an experiment adapter wraps TCloudSandbox.create() as though it were a raw Sandbox SDK client, these fields can be narrowed/dropped/mis-shaped. Example: backend is typed as a string in TCloudSandboxCreateOptions, while runLoop passes the SDK backend object. env is not represented by the tcloud helper shape either.

Why it matters

In agent-lab AEC experiments, the treatment arm is file-backed and validated through box.fs.read('output.md'). We can safely use:

  • TCloudClient.fromCliBridge(...) for prompt-only direct-chat baselines.
  • raw @tangle-network/sandbox new Sandbox(...) for file-backed runLoop arms.

We cannot honestly use TCloudSandbox as the runLoop sandbox client yet without risking fake success, because the runtime needs the full SDK create contract preserved.

Requested fix

Add one of these explicit surfaces:

  1. A SDK-compatible client adapter:
const client = TCloud.sandboxClient({ apiKey, baseUrl, timeoutMs })
await client.create(fullCreateSandboxOptions) // returns SandboxInstance directly
  1. Or expose the underlying SDK client intentionally:
const tcloudSandbox = new TCloudSandbox(...)
tcloudSandbox.sdk.create(fullCreateSandboxOptions)
  1. Or add a createRaw(options: CreateSandboxOptions): Promise<SandboxInstance> method that preserves the full object exactly, while create(options: TCloudSandboxCreateOptions) remains the convenience/attestation wrapper.

Acceptance checks

  • Unit test proves full CreateSandboxOptions.backend.model, backend.profile, env, and resources survive unchanged into the underlying Sandbox.create() call.
  • Type test proves the returned object can satisfy agent-runtime's SandboxClient interface: create(options?: CreateSandboxOptions): Promise<SandboxInstance>.
  • TEE convenience wrapper behavior remains unchanged for current TCloudSandbox.create() users.

Discovered while

Wiring agent-lab AEC campaign execution targets. The lab now fails closed if someone sets the file-backed treatment to cli-bridge or tcloud-sandbox; direct chat through TCloudClient.fromCliBridge is validated separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions