September 25, 2026
GMI Agentbox hosts long-running research agents with runtime and model access on one GMI Cloud account and network. It is in early access and combines runtime and model billing on one invoice.
A multi-day run needs three independent safeguards:
| Elapsed time | What the run is doing | What the platform has to get right |
|---|---|---|
| Hour 1 | Planning, first search passes, first few hundred model calls | A runtime lifetime longer than the default, and a renewal path that does not race the expiry |
| Hour 24 | Partial findings accumulated, several tool chains deep | State that survives a container restart, because nothing useful lives in memory anymore |
| Hour 72 | Synthesis, long-context passes over everything gathered | Model calls that have not drifted onto a second vendor, a second key, and a second bill |
GMI Cloud combines GPU infrastructure, model serving, and agent runtimes on one platform.
A 72-hour run can fail through lease expiry, loss of in-memory state after restart, or model calls crossing a vendor boundary. Address each separately.
timeout is "omitted or 0 or less, 300 seconds is used" (Create a sandbox). A three-day run is 864 default windows long. Something has to push that expiry forward, reliably, for 72 hours.Agentbox lists ephemeral, long-running, and always-on tiers. Long-running covers persistent state across multi-step jobs, including deep research, document pipelines, and routing; always-on retains memory and identity across sessions. The product page advertises sessions 30× longer than a 24-hour sandbox. (GMI Agentbox)
Renew the lease while work continues; a create-time timeout alone does not sustain a multi-day run.
POST /sandboxes/{id}/timeout sets TTL in seconds from the current time. Its four relevant properties are: (Set sandbox timeout)
old_end_at and new_end_at. Schedule the next renewal against new_end_at, not against your own clock arithmetic.409. That status is the signal to stop the run and resume from a checkpoint, not to retry.sandbox_access_token on every connect; do not reuse values cached from a previous create/connect."Prefer POST /sandboxes/{id}/connect for keepalive: its timeout is extend-only, and an extension returns the re-signed token, end_at, sandbox_key, and domain. Use /timeout for an independent reset or a shorter TTL, then reconnect to refresh credentials. (Connect to a sandbox)
Choose a TTL and renewal interval together. Their ratio controls retry headroom; the TTL caps how long an abandoned runtime remains billable.
For a 72-hour run (259,200 seconds):
| TTL per renewal | Renewal interval | Renewal attempts inside each TTL window | Renewal calls across the run | Worst-case orphan window if the orchestrator dies |
|---|---|---|---|---|
| 300 s (the default) | 60 s | 4 | 4,320 | 5 minutes |
| 1,800 s | 300 s | 5 | 864 | 30 minutes |
| 3,600 s | 600 s | 5 | 432 | 1 hour |
| 21,600 s | 1,800 s | 11 | 144 | 6 hours |
Keep TTL at least four times the renewal interval, capped by the acceptable orphan cost. A 3,600-second TTL renewed every 600 seconds needs 432 calls over 72 hours, leaves five further attempts per window, and limits an orchestrator crash to at most one extra billable hour.
The loop handles renewal, credential rotation, failures, and teardown:
import os
import random
import re
import threading
from datetime import datetime, timezone
import requests
CONTROL_PLANE = "https://console.gmicloud.ai/api/v2"
HEADERS = {"Authorization": f"Bearer {os.environ['GMI_SANDBOX_API_KEY']}"}
TTL_SECONDS = 3600 # lifetime each renewal grants
RENEW_EVERY = 600 # five more attempts fit inside every TTL window
SAFETY_MARGIN = 30 # stop trusting the lease this long before it expires
HTTP_TIMEOUT = 10
RETRYABLE = {429, 500, 502, 503, 504}
_FRACTION = re.compile(r"(?<=:\d\d)\.(\d+)") # RFC 3339 allows any number of digits
class SandboxGone(RuntimeError):
"""The runtime is no longer usable, so the run must resume from a checkpoint."""
def _expiry(value):
"""Parse an RFC 3339 expiry, tolerating nanoseconds and rejecting naive values."""
if not isinstance(value, str):
raise ValueError(f"expected a timestamp string, got {type(value).__name__}")
normalized = _FRACTION.sub(lambda m: "." + m.group(1)[:6].ljust(6, "0"), value)
parsed = datetime.fromisoformat(normalized.replace("Z", "+00:00"))
if parsed.tzinfo is None:
raise ValueError(f"timestamp carries no UTC offset: {value}")
return parsed
def connect(sandbox_id, ttl=TTL_SECONDS, http_timeout=HTTP_TIMEOUT):
"""Extend the lease and return the credentials the extension just re-signed."""
response = requests.post(
f"{CONTROL_PLANE}/sandboxes/{sandbox_id}/connect",
headers=HEADERS, json={"timeout": ttl}, timeout=http_timeout,
)
if response.status_code in (404, 409):
raise SandboxGone(f"connect returned {response.status_code}")
response.raise_for_status()
payload = response.json()
data = payload.get("data") if isinstance(payload, dict) else None
if not isinstance(data, dict):
raise ValueError("connect response carried no data object")
return data, _expiry(data["end_at"])
class Lease:
"""Thread-safe holder for the credentials and expiry that renewal keeps replacing."""
def __init__(self, data, deadline):
self._lock = threading.Lock()
self.stop = threading.Event()
self.failure = None
self.refresh(data, deadline)
def refresh(self, data, deadline):
# build the whole snapshot first: a missing field must not half-apply
snapshot = (f"https://{data['sandbox_key']}.{data['domain']}",
data["sandbox_access_token"], deadline)
with self._lock:
self._base_url, self._token, self._deadline = snapshot
def credentials(self):
with self._lock:
return self._base_url, self._token
def headroom(self):
with self._lock:
deadline = self._deadline
return (deadline - datetime.now(timezone.utc)).total_seconds() - SAFETY_MARGIN
def give_up(self, reason):
self.failure = reason
self.stop.set()
def keepalive(sandbox_id, lease):
"""Thread entry point. However this ends, the lease is left signalled."""
try:
_renew_until_stopped(sandbox_id, lease)
except BaseException as exc: # nothing raised in a worker reaches the caller
lease.give_up(exc)
raise
finally:
lease.stop.set() # the main loop must never wait on a dead worker
def _renew_until_stopped(sandbox_id, lease):
backoff = 1.0
while not lease.stop.is_set():
headroom = lease.headroom()
if headroom <= 0:
return lease.give_up("ran out of renewal headroom")
try:
# bound the request so a hung call cannot outlive the lease it renews
data, deadline = connect(sandbox_id, http_timeout=min(HTTP_TIMEOUT, headroom))
lease.refresh(data, deadline)
backoff, wait_for = 1.0, RENEW_EVERY
except SandboxGone as exc:
return lease.give_up(exc)
except (requests.RequestException, KeyError, TypeError, ValueError) as exc:
status = getattr(getattr(exc, "response", None), "status_code", None)
if status is not None and status not in RETRYABLE:
return lease.give_up(exc)
wait_for = backoff
backoff = min(backoff * 2, 60.0)
remaining = lease.headroom()
if remaining <= 0:
return lease.give_up("ran out of renewal headroom")
lease.stop.wait(min(wait_for, remaining) * random.uniform(0.85, 1.0))
def teardown(sandbox_id):
"""Best effort. Delete acceptance is the billing cutoff, so never skip it."""
try:
response = requests.delete(
f"{CONTROL_PLANE}/sandboxes/{sandbox_id}",
headers=HEADERS, timeout=HTTP_TIMEOUT,
)
if response.status_code not in (202, 404):
print(f"delete not accepted ({response.status_code}); sweep this id later")
except requests.RequestException as exc:
print(f"delete call failed ({exc}); sweep this id later")
def run_research_agent(sandbox_id, steps, execute, checkpoint):
lease = None
try:
lease = Lease(*connect(sandbox_id))
threading.Thread(target=keepalive, args=(sandbox_id, lease), daemon=True).start()
for step in steps:
if lease.stop.is_set():
raise SandboxGone(f"lease lost, resume from the checkpoint: {lease.failure}")
base_url, token = lease.credentials() # re-read, renewal replaces both
checkpoint(step, execute(base_url, token, step))
if lease.stop.is_set(): # also check after the last step
raise SandboxGone(f"lease lost during the final step: {lease.failure}")
finally:
if lease is not None:
lease.stop.set()
teardown(sandbox_id) # runs even if connect never succeeded
The loop renews through connect and updates all three credentials atomically. A shared stop flag carries worker-thread failure to the main loop, which checks it before each step and after the last. Requests are bounded by remaining lease time; jitter prevents synchronized renewals. Teardown runs even if initialization fails. DELETE returns 202 and is replayable while deletion is already underway. (Delete a sandbox)
Tag each sandbox with the run ID. GET /sandboxes supports AND-ed metadata filters so fleet cleanup can find orphaned instances. (List active sandboxes)
A multi-day research agent keeps its state outside the container, in a store you control, written at every step boundary.
Use an async job pattern: return job_id with HTTP 202, run work in the background, and poll status every 3–5 seconds. Persist state to Redis or a database, not memory, and inject store credentials as Secrets during agent registration. (Handle long-running requests)
Three platform facts shape how you lay that out:
vcpu, memory_mb, and disk_mb are immutable after a sandbox is created (Get sandbox details). You cannot grow a long-running agent's memory mid-run, so size the template for the worst step in the plan, not the average one.GET /sandboxes/{id} reports provisioning, running, checkpointing, updating, or failed, plus the authoritative end_at and a failure object that is "Non-empty only when state=failed." A supervisor that polls this is how you distinguish "the agent is thinking" from "the runtime is gone".https://console.gmicloud.ai/api/v2 with a bearer API key; commands and files go to https://{sandbox_key}.{domain} with an X-Access-Token header, and the two credential types are not interchangeable (Sandbox API overview). The Python SDK (pip install gmi-sandbox-sdk) wraps both, including sandbox.files.upload() and sandbox.files.download() for moving checkpoints in and out (SDK usage).For long idle gaps, compare keeping the runtime alive with checkpointing, deleting, and resuming it.
Hosted Agentbox containers are billed for their full running lifetime, including idle time, until DELETE /v1/containers/{id}. Auto-pause is under exploration. For Sandbox v2, accepted deletion time is the usage-accounting cutoff. (Agentbox FAQ; Isolation is the easy half of the sandbox problem)
Consider a 72-hour run with 11 active hours and 61 waiting on sources, embargoes, or review:
| Design | Billable runtime hours | Relative cost |
|---|---|---|
| One runtime held open for the whole run | 72 | 1.00× |
| Checkpoint to external state, delete between phases, resume on a fresh runtime | 11 | 0.15× |
Delete and resume when an idle gap exceeds checkpoint-write plus recreation-and-reload time. GMI's public-network cold-create test reports 0.21 seconds median and 0.26 seconds p95 to Running, but restoring your state adds time. Measure your complete recovery cycle rather than applying the create benchmark alone. (Isolation is the easy half of the sandbox problem)
GMI's Verified configuration pairs Cluster Engine compute with GMI Models inference. The same-network claim applies to that deployment configuration:
Build toward the Verified configuration and record the serving route during onboarding when same-network execution is mandatory.
GMI MaaS describes models hosted on GMI's data center infrastructure. Long-context open-weight models can support later synthesis passes over accumulated research. The following are October 2026 list prices:
| Model ID | Context | Input per 1M tokens | Output per 1M tokens |
|---|---|---|---|
deepseek-ai/DeepSeek-V4-Flash-0731 | ~1M tokens | $0.44 | $1.32 |
Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 | ~262K tokens | $0.90 | $4.50 |
zai-org/GLM-5.1-FP8 | 202,752 tokens | $1.40 | $4.40 |
Check the model library before budgeting. DeepSeek-V4-Flash-0731 had a 35% discount on October 15, 2026, at $0.286/$0.858; the table uses undiscounted rates. Confirm and record each model's serving route when network residency is a requirement. (console.gmicloud.ai/model-library)
At 1,200 calls averaging 20K input and 2K output tokens, a run uses 24M input and 2.4M output tokens. At list rates, DeepSeek-V4-Flash-0731 costs $10.56 + $3.17 = $13.73, Qwen3-Coder-480B-A35B-Instruct-FP8 costs $32.40, and GLM-5.1-FP8 costs $44.16. Ten times the traffic multiplies each figure by ten; model choice changes inference spend by over 3x without changing runtime hours.
The catalog also provides proprietary frontier models through one account, OpenAI-compatible API, and invoice. For weights that must run on reserved single-tenant GPUs, use GMI Cloud Prime Inference under the same account and agree on the serving configuration explicitly.
Evaluate Agentbox for runtime and MaaS for models against these seven requirements:
| # | Requirement | Where it lands on GMI Cloud |
|---|---|---|
| 1 | A documented way to extend a running session, with the new expiry returned | POST /sandboxes/{id}/connect and POST /sandboxes/{id}/timeout, returning end_at and old_end_at / new_end_at |
| 2 | A documented credential-refresh rule after an extension | Call connect again and overwrite all three cached values |
| 3 | An explicit statement of what happens to in-container state | "GMI containers are stateless", with the async job pattern and external store prescribed in docs |
| 4 | Observable lifecycle states and an authoritative expiry field | GET /sandboxes/{id} returning state and end_at |
| 5 | Model access on the same network as the runtime | "Calls stay on GMI's own network.", with the Verified criterion defining what qualifies |
| 6 | One invoice across runtime and inference, and one key across models | "1 invoice" covering "Runtime + models + billing"; "200+ Models available in one key" |
| 7 | A stated billing cutoff you control | Billed until DELETE is called, with delete acceptance as the accounting cutoff |
Agentbox provides per-user isolated runtimes; MaaS supplies 200+ models. For dedicated capacity, October 2026 starting GPU rates are H100 $2.60/hour and H200 $3.20/hour. Agent registration currently lists a 2 vCPU, 4 GB RAM container with 10 GiB ephemeral and 30 GiB data storage in IOWA IDC-1 (US-IA, US); more tiers and regions are planned. Agentbox remains in early access. Confirm that the largest step fits the available tier, region, and onboarding path. (GMI Agentbox; GMI MaaS; GMI Cloud GPU infrastructure; Register an agent)
Companion articles cover hosted-agent runtime logs, usage, and cost dashboards, and Grok 4.6 for long-running workflows. (runtime logs, usage, and cost dashboards for hosted agents; Grok 4.6: A New Option for Long-Running Agent Workflows)
Can a sandbox on GMI Cloud stay alive for days? Yes, if the lease is renewed while the sandbox remains running. The default timeout is 300 seconds. Both connect and timeout can extend lifetime; connect also returns refreshed credentials. The Agentbox page positions the service for seconds-to-days workloads and advertises sessions 30× longer than a 24-hour sandbox.
What happens to my agent's state if the container restarts? In-memory state is lost. Persist job state externally, inject store credentials as Secrets, and checkpoint at every step boundary.
Do I have to re-fetch credentials after extending a session? Yes. A lifetime extension may re-sign the access token. Reconnect and replace sandbox_key, domain, and sandbox_access_token together; renewing through connect performs both operations in one call.
What does same-network inference mean on Agentbox? The documented Verified setup uses GMI Cluster Engine for compute and GMI Models for inference on GMI's GPU fleet, keeping execution and model calls within GMI's network. Confirm that configuration for your deployment.
Am I billed while a long-running agent sits idle? Yes. Hosted containers remain billable while idle until deletion; auto-pause is not a current assumption. Checkpoint and resume across long gaps when recovery costs less than holding the runtime.
Do I need a second vendor for model access? No. Runtime and access to 200+ models share one account and invoice, with separate usage line items. Add Prime Inference when reserved single-tenant model serving is needed.
Is GMI Agentbox generally available? No. It is labeled early access, although published workloads include Oqoqo's evaluation runs at up to 50,000 concurrent sandboxes. Confirm tiers, regions, and onboarding for your workload. (GMI Agentbox)
Test one 72-hour private run and record renewals, checkpoints, tokens, and billable hours. Start in the Console with one sandbox and a 3,600-second lease, or ask the GMI Cloud team to size the runtime tier, region, and model mix. (talk to the GMI Cloud team)
Colin Mo
GMI Cloud helps you architect, deploy, optimize, and scale your AI strategies
