Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions omnigent/onboarding/sandboxes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"lakebox": "omnigent.onboarding.sandboxes.lakebox:LakeboxLauncher",
"modal": "omnigent.onboarding.sandboxes.modal:ModalSandboxLauncher",
"daytona": "omnigent.onboarding.sandboxes.daytona:DaytonaSandboxLauncher",
"remote": "omnigent.onboarding.sandboxes.remote:RemoteSandboxLauncher",
"boxlite": "omnigent.onboarding.sandboxes.boxlite:BoxliteSandboxLauncher",
# CoreWeave Sandbox via the official cwsandbox SDK (the
# `omnigent[cwsandbox]` extra), imported lazily like modal/daytona.
Expand Down
21 changes: 21 additions & 0 deletions omnigent/onboarding/sandboxes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,14 @@ class SandboxLauncher(ABC):
# of being silently revived onto an empty workspace.
can_resume: ClassVar[bool] = False

def set_launch_context(self, *, owner: str, session_id: str | None) -> None:
"""Bind authenticated launch metadata to this launcher instance.

Providers that delegate provisioning to an external control plane can
override this hook. Direct providers ignore it by default.
"""
del owner, session_id

@abstractmethod
def prepare(self) -> None:
"""
Expand Down Expand Up @@ -657,6 +665,19 @@ def keep_alive(self, sandbox_id: str) -> None:
"""
raise self._capability_error("configure keep-alive")

def set_activity(self, sandbox_id: str, *, active: bool) -> None:
"""Tell a lifecycle-aware provider whether the sandbox has active work.

The default is deliberately a no-op: most providers either infer
activity themselves or do not expose a policy hook. Managed remote
controllers can override this to protect a long-running turn from an
idle timer and restore the normal idle policy when the turn finishes.

:param sandbox_id: Target sandbox.
:param active: ``True`` while a turn or background task is running.
"""
del sandbox_id, active

@abstractmethod
def run(self, sandbox_id: str, command: str, *, check: bool = True) -> RemoteCommandResult:
"""
Expand Down
114 changes: 1 addition & 113 deletions omnigent/onboarding/sandboxes/daytona.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@

from __future__ import annotations

import json
import os
import time
import uuid
from collections.abc import Sequence
from typing import TYPE_CHECKING, ClassVar
from urllib.parse import urlencode
from urllib.request import Request, urlopen

import click

Expand Down Expand Up @@ -218,8 +215,6 @@ def __init__(self, *, image: str | None = None, env: Sequence[str] | None = None
self._env_names = tuple(env) if env is not None else None
self._client: daytona_sdk.Daytona | None = None
self._sandboxes: dict[str, DaytonaSandbox] = {}
self._platform_owner: str | None = None
self._platform_session_id: str | None = None

def _daytona(self) -> daytona_sdk.Daytona:
"""
Expand Down Expand Up @@ -303,56 +298,6 @@ def _resolve_sandbox_env(self) -> dict[str, str]:
resolved[name] = value
return resolved

def set_platform_owner(self, owner: str) -> None:
"""Bind this per-launch instance to the authenticated session owner."""
self._platform_owner = owner

def set_platform_session(self, session_id: str) -> None:
"""Bind provider metadata to the Omnigent session being launched."""
self._platform_session_id = session_id

def _resolve_owner_profile(self) -> dict[str, object]:
"""Resolve non-secret Daytona mount references for this owner."""
base_url = os.environ.get("PLATFORM_MODEL_CREDENTIAL_BROKER_URL")
token = os.environ.get("PLATFORM_MODEL_CREDENTIAL_BROKER_TOKEN")
if not base_url and not token:
return {}
if not base_url or not token or not self._platform_owner:
raise click.ClickException(
"owner-scoped model credential broker is incompletely configured"
)
separator = "&" if "?" in base_url else "?"
url = f"{base_url}{separator}{urlencode({'owner': self._platform_owner})}"
request = Request(
url,
headers={
"Accept": "application/json",
"Authorization": f"Bearer {token}",
},
)
try:
with urlopen(request, timeout=10) as response:
profile = json.loads(response.read().decode("utf-8"))
except Exception as exc:
raise click.ClickException(
"could not resolve the execution owner's model connection profile"
) from exc
if not isinstance(profile, dict):
raise click.ClickException("model connection broker returned an invalid profile")
return profile

@staticmethod
def _append_runner_passthrough(env_vars: dict[str, str]) -> None:
names = [
item.strip()
for item in env_vars.get("OMNIGENT_RUNNER_ENV_PASSTHROUGH", "").split(",")
if item.strip()
]
for name in ("CODEX_HOME", "CLAUDE_CONFIG_DIR"):
if name not in names:
names.append(name)
env_vars["OMNIGENT_RUNNER_ENV_PASSTHROUGH"] = ",".join(names)

def prepare(self) -> None:
"""
Local preflight: the Daytona SDK must be installed and an API
Expand Down Expand Up @@ -388,52 +333,17 @@ def provision(self, name: str) -> str:

resolved_ref = self._image_ref or os.environ.get(HOST_IMAGE_ENV_VAR) or DEFAULT_HOST_IMAGE
env_vars = self._resolve_sandbox_env()
profile = self._resolve_owner_profile()
env_vars.setdefault("CODEX_HOME", "/root/.codex")
env_vars.setdefault("CLAUDE_CONFIG_DIR", "/root/.claude")
self._append_runner_passthrough(env_vars)
volumes = None
raw_volume = profile.get("volume")
if raw_volume is not None:
if not isinstance(raw_volume, dict):
raise click.ClickException("model connection profile volume is invalid")
volume_id = raw_volume.get("id")
subpath = raw_volume.get("subpath")
mount_path = raw_volume.get("mountPath", "/root/.tellimer-auth")
if not all(
isinstance(value, str) and value for value in (volume_id, subpath, mount_path)
):
raise click.ClickException("model connection profile volume is incomplete")
volumes = [
daytona.VolumeMount(
volume_id=volume_id,
mount_path=mount_path,
subpath=subpath,
)
]
raw_secrets = profile.get("secrets", {})
if not isinstance(raw_secrets, dict) or not all(
isinstance(key, str) and isinstance(value, str) for key, value in raw_secrets.items()
):
raise click.ClickException("model connection profile secrets are invalid")
labels = {"omnigent-name": name}
if self._platform_session_id is not None:
# Provider-dashboard audit trail: the canonical session id makes
# the one-session/one-sandbox ownership visible outside Omnigent.
labels["omnigent-session-id"] = self._platform_session_id
click.echo(f"▸ Creating Daytona sandbox '{name}' from {resolved_ref}")
try:
handle = self._daytona().create(
daytona.CreateSandboxFromImageParams(
image=resolved_ref,
env_vars=env_vars or None,
labels=labels,
labels={"omnigent-name": name},
# Release compute after one hour of inactivity. The
# persistent sandbox is resumed in place on next message.
auto_stop_interval=_MANAGED_AUTO_STOP_MINUTES,
resources=daytona.Resources(cpu=_SANDBOX_CPU, memory=_SANDBOX_MEMORY_GIB),
volumes=volumes,
secrets=raw_secrets or None,
),
timeout=_CREATE_TIMEOUT_S,
# First-use image pulls stream build logs; echo them so a
Expand All @@ -448,28 +358,6 @@ def provision(self, name: str) -> str:
# instead of a generic "internal error".
raise click.ClickException(f"Daytona sandbox creation failed: {exc}") from exc
self._sandboxes[handle.id] = handle
if raw_volume is not None:
self.run(
handle.id,
"set -eu; mkdir -p /root/.codex; chmod 700 /root/.codex; "
"if [ -s /root/.tellimer-auth/codex/auth.json ]; then "
"cp -f /root/.tellimer-auth/codex/auth.json /root/.codex/auth.json; "
"chmod 600 /root/.codex/auth.json; fi; "
"if [ -f /root/.tellimer-auth/codex/config.toml ]; then "
"cp -f /root/.tellimer-auth/codex/config.toml /root/.codex/config.toml; fi; "
"rm -f /root/.tellimer-auth/codex/state_*.sqlite*",
)
self.run_background(
handle.id,
"while sleep 15; do "
"if [ -s /root/.codex/auth.json ]; then "
"mkdir -p /root/.tellimer-auth/codex; "
"tmp=/root/.tellimer-auth/codex/.auth.json.$$.tmp; "
"cp -f /root/.codex/auth.json $tmp && chmod 600 $tmp "
"&& mv -f $tmp /root/.tellimer-auth/codex/auth.json; "
"fi; done",
log_path="/tmp/codex-auth-sync.log",
)
click.echo(f" → created {handle.id}")
return handle.id

Expand Down
Loading
Loading