Sessions & environments
A one-shot /v1/exec runs in a throwaway VM and keeps nothing. An RL environment is
driven over many steps, with state carrying forward. A session is one live VM held
alive across calls: files and the working tree persist between steps, so an agent can search, edit,
test, and repeat against the same machine. This is the shape an environment's step()
loop needs, and it is the primary way to run Collimate in production.
Stateless exec vs. a session
/v1/exec | Session | |
|---|---|---|
| Lifetime | One throwaway VM per call | One live VM, many calls |
| State between calls | None, fresh each time | Persists (files, working tree) |
| Per-call spawn cost | A new VM each call | Spawned once, then ~0 |
| Best for | Independent samples, batch fan-out | Multi-step agents, RL step() loops |
Between calls a session's vCPUs are descheduled, so the VM sits idle until you drive the next step, then resumes in microseconds.
Lifecycle at a glance
POST /v1/sessions create one live VM from a template -> session_id │ ▼ (repeat, state persists) POST /v1/sessions/{id}/exec run code · run commands · write files │ ▼ GET /v1/sessions/{id} status (age, idle, exec_count) GET /v1/sessions list live sessions │ ▼ DELETE /v1/sessions/{id} destroy the VM, reclaim memory
Create a session
Spawns one live VM from a template and holds it open. template_id is required.
POST /v1/sessions
Authorization: Bearer col_live_...
Content-Type: application/json
{ "template_id": "my-env" }
201 Created
{
"session_id": "sess_019cf68f0a1c7e2b...",
"template_id": "my-env",
"fork_time_ms": 1.3,
"create_time_ms": 64.2
}
400 if absent.sess_…) used for every later call on this VM.Run a step
Drive the live VM. Send one of three payloads (or combine files with commands). State from previous steps is always visible. The response shape matches /v1/exec, except there is no per-call spawn, so fork_time_ms is 0.
Run code
POST /v1/sessions/{id}/exec
{ "code": "import os; print(os.listdir('/app'))", "timeout_seconds": 30 }
200 OK
{ "id": "...", "stdout": "['src', 'tests']\n", "stderr": "", "exit_code": 0,
"fork_time_ms": 0, "exec_time_ms": 12.4, "total_time_ms": 12.4 }
Run commands
Each command is an argv array, run from the working directory.
POST /v1/sessions/{id}/exec
{ "commands": [ ["grep", "-rn", "bug", "src/"], ["python", "-m", "pytest", "-x"] ] }
Write files
Write or overwrite files in the live VM, for example applying an edit before re-running tests.
POST /v1/sessions/{id}/exec
{ "files": [ { "path": "/app/src/handler.py", "content": "def handle(): ..." } ] }
Each command runs fresh from the working directory, so file edits persist but shell-process state (a bare cd, exported vars) does not, exactly like docker exec. Use absolute paths, or wrap in bash -lc '…'.
Inspect & list
GET /v1/sessions/{id}
200 OK
{ "session_id": "sess_...", "template_id": "my-env",
"age_secs": 184, "idle_secs": 12, "exec_count": 7 }
GET /v1/sessions
200 OK
{ "sessions": [ { "session_id": "sess_...", "idle_secs": 12, "exec_count": 7 }, ... ] }
Destroy a session
Destroys the VM and reclaims its memory. By default an in-flight exec is allowed to finish first; pass ?force=true to abort it immediately.
curl -X DELETE 'https://api.collimate.ai/v1/sessions/'$SID'?force=true' \
-H 'Authorization: Bearer col_live_...' # -> 204 No Content
End-to-end example
A full step loop over one session: search, edit, test, then reclaim the VM.
# 1. open one live VM
SID=$(curl -s -X POST https://api.collimate.ai/v1/sessions \
-H 'Authorization: Bearer col_live_...' \
-d '{"template_id":"my-env"}' | jq -r .session_id)
# 2. step through it, state carries over
curl -X POST https://api.collimate.ai/v1/sessions/$SID/exec \
-d '{"commands":[["grep","-rn","TODO","src/"]]}'
curl -X POST https://api.collimate.ai/v1/sessions/$SID/exec \
-d '{"files":[{"path":"/app/src/fix.py","content":"# patched"}]}'
curl -X POST https://api.collimate.ai/v1/sessions/$SID/exec \
-d '{"commands":[["python","-m","pytest","-x"]]}'
# 3. reclaim the VM
curl -X DELETE https://api.collimate.ai/v1/sessions/$SID
An RL environment in a few lines
Wrap the lifecycle in a Gym-style environment: reset() opens a session, step() drives it, close() reclaims it.
class SandboxEnv:
def __init__(self, client, template_id):
self.client, self.template_id, self.sid = client, template_id, None
def reset(self):
self.sid = self.client.open_session(self.template_id) # one VM
return self.observe()
def step(self, action):
result = self.client.session_exec(self.sid, action) # state persists
return self.observe(), self.score(result), self.is_done(result), {}
def close(self):
if self.sid:
self.client.close_session(self.sid) # reclaim VM
Each environment instance is a real, isolated VM. Run hundreds concurrently on one host; the memory cost is only the pages each one changes.
Lifecycle, limits & status codes
429.col_live_… token; 100 requests/second per key.BYOC deployments can tune the live-session cap, idle TTL, reaper interval, and bring-up timeouts; ask us for the tuning reference.
template_id on create.