HarborBackend runs an Osmosis AgentWorkflow or a registered native Harbor agent inside a Harbor trial. Every rollout gets a copied task directory, a task-defined environment, and Harbor-managed agent and verifier phases.
Install and Import
Install the Harbor and rollout-server features:uv builder and the backend prefers the executable installed with the active Python interpreter. A separate global uv installation is not required.
Import the backend from its Harbor submodule:
HarborBackend is not re-exported from osmosis_ai.rollout or osmosis_ai.rollout.backend. From 0.3.7, the harbor extra installs Harbor together with its Daytona and OpenSandbox environment dependencies, so neither provider requires additional packages.
Harbor Version Compatibility
From0.3.8, the harbor extra supports Harbor >=0.20.0,<0.25. Upgrading Harbor alone does not change the SDK’s published harbor-v1 source-image identities or require rebuilding those images.
When moving existing tasks or native agent configuration to Harbor 0.23:
- Remove unsupported
native_agent_kwargs; agents with an options schema reject unknown keys and invalid types. - Use portable step names that remain unique when compared case-insensitively, and repository-relative Git task paths that stay inside the repository.
- Make every value in a native verifier’s
reward.jsona finite JSON number, such as{"reward": 1}; quoted numbers and non-numeric fields fail parsing. The SDK grader already writes numeric rewards. - Allow Mini-SWE setup to download its managed Python 3.12 toolchain.
Choose a Task Mode
task_mode determines who owns the instruction, how the default task is selected, and whether the request prompt reaches the task.
Both modes use Harbor trials and provide the same isolation. Mode selection changes task semantics, not the sandbox technology.
Template Mode
Template mode starts every rollout from one task directory. When the request contains a prompt, the backend copies the task and replacesinstruction.md with that prompt before Harbor starts the trial. An AgentWorkflow reads the original messages from ctx.prompt; How Native Agents Receive the Prompt describes what a native agent reads.
instruction.md file is optional for the configured template when every rollout supplies a prompt. Add a placeholder instruction if you also want the directory to pass Harbor’s standalone task validation. A tests/test.sh is optional when the backend supplies an Osmosis Grader.
docker_image in task.toml. See Managed and Self-Hosted Environments for that requirement and for Daytona.
Template mode is the Harbor path most similar to LocalBackend: one workflow handles changing dataset prompts. Choose it when those prompts need the same isolated operating system, tools, services, or filesystem setup on every rollout.
How Native Agents Receive the Prompt
A native Harbor agent receives one task string rather than a message list. The agent places that string in its own prompt as the user’s task and keeps its own system prompt: Terminus 2 inserts it underTask Description:, mini-swe-agent passes it as --task, and OpenCode sends it as the opencode run message.
When every message has the system or user role, string content, and no other fields, the backend writes the message contents to instruction.md in order, separated by a blank line and without role labels. A dataset system_prompt therefore becomes the opening of the task text, not the agent’s system prompt.
Prompts with other roles, non-string content such as images, or extra message fields keep the JSON form so no structured information is lost. This fallback is not conversation replay or native multimodal input. Dataset mode always keeps the task’s authored instruction.
For example, this dataset row:
instruction.md:
Dataset Mode
Dataset mode treats each Harbor task as the complete unit of work. The task retains its owninstruction.md, environment, configuration, and verifier.
0.3.8, you can omit the task IDs in prewarm_lifespan() to discover tasks under tasks_dir automatically. Pass IDs explicitly to prewarm a subset or to support earlier SDK versions.
Each Osmosis metadata-mode dataset row selects the Harbor task by directory name:
AgentWorkflow. The task’s verifier normally computes the reward; if tests/test.sh is absent, a configured Osmosis Grader supplies the verifier instead.
Integrate an Existing Harbor Dataset
If a local dataset already works withharbor run, its task directories are the reusable boundary:
1
Keep the task directories
Point
tasks_dir at the local dataset root. Each child must be a Harbor task with task.toml, instruction.md, an environment/, and the verifier files required by that task.2
Choose a supported agent track
Use
agent="terminus-2", "mini-swe-agent", "opencode", "pi", or "oracle" when that registered native agent fits. Otherwise package your agent behavior as an Osmosis AgentWorkflow and pass its class or import path.3
Create the selector dataset
Upload an Osmosis metadata-mode dataset whose
harbor_task_id values match the task directory names. The Platform dataset selects trials; the Harbor task directories remain with the rollout code.4
Run evaluation before training
Confirm that the native verifier emits the
reward channel and that trainable agents produce a usable trajectory before starting training.This path reuses task content, not the complete Harbor job definition.
HarborBackend does not read Harbor JobConfig, dataset filters, attempt counts, arbitrary agent configs, or harbor run CLI flags. Osmosis supplies request scheduling, the model endpoint, result polling, and rollout identity.Dynamic Task Sources
In either mode, a request can setmetadata["harbor_task"] instead of using the configured default selection:
Pin package references and Git commits for reproducibility. Instruction ownership still follows
task_mode: template mode replaces the fetched task’s instruction, while dataset mode keeps it.
Agent Tracks
agent selects one of two execution tracks:
An
AgentWorkflow project must contain pyproject.toml and an importable Python package, and the task image must support Python. Pass code_dir when the project cannot be inferred, or pass a prebuilt bundle wheel. Keep the task image (its docker_image or Dockerfile) focused on task dependencies; the backend installs the rollout bundle and can preinstall its declared dependencies into the copied image.
Registered native names:
Use
workflow_config with an AgentWorkflow. Use native_agent_kwargs only with a registered native agent. model_name defaults to openai/osmosis-rollout; per-request metadata can override it with harbor_model.
For agent="mini-swe-agent", pass native_agent_kwargs={"reasoning_effort": "high"} to set reasoning effort while retaining the rollout Chat Completions endpoint. The top-level effort takes precedence over the effort in an inline config or YAML config_file; other settings are preserved. config and config_file are mutually exclusive.
Native OpenCode
Starting with0.3.4, use agent="opencode" to run existing Harbor tasks. The SDK connects OpenCode to the active rollout model endpoint and disables automatic compaction and pruning to preserve the training trajectory.
create_rollout_server() as in the dataset-mode example above; managed runs receive their sandbox credentials from the Platform, as described below. To pin OpenCode, pass native_agent_kwargs={"version": "1.18.27"}; the pin applies during execution and prewarm. Pass additional OpenCode configuration through native_agent_kwargs["opencode_config"]; the SDK controls endpoint, authentication, and compaction settings.
From 0.3.8, OpenCode reuses a working runtime preinstalled under /opt/osmosis-agent/bin when stdbuf is available. If you pin a version, the installed version must match. Otherwise setup falls back to installing OpenCode. This applies to execution and prewarm.
model_name must use the non-empty provider/model form; the default openai/osmosis-rollout already satisfies this. Override it per request with metadata["harbor_model"]; null, empty, and non-string values are rejected.
Native Pi
Useagent="pi" to run existing Harbor tasks with the Pi coding agent. The SDK connects Pi to the active rollout chat-completions endpoint and disables Pi’s automatic context compaction so the training trajectory stays append-only.
subagent extension with two subagent definitions, general and worker. Pi can delegate work by calling its subagent tool, for example {"agent": "general", "task": "<child prompt>"} for one child or {"tasks": [...]} for parallel children. Each subagent inherits the rollout model, so child Pi processes call the same endpoint as the parent. The SDK converts Pi’s session log into the rollout trajectory, including token usage reported by subagent runs.
As with OpenCode, model_name must use the non-empty provider/model form.
Reward Source and Precedence
Harbor always treats a task-provided verifier as authoritative:
There is no
custom_tests_dir in v0.3. Keep native verifier files under each task’s tests/ directory. A native verifier must write Harbor’s reward channel, normally through /logs/verifier/reward.txt or reward.json with a numeric reward key (Harbor 0.23 requires every value in that file to be a finite number). The backend does not guess another channel.
Constructor Reference
Prewarming, Capacity, and Cancellation
prewarm_lifespan() runs prewarm before the server accepts traffic. You can also call await backend.prewarm() directly. Dockerfile builds depend on the selected environment; OpenSandbox uses prebuilt images. Template mode prewarms its configured task. From 0.3.8, dataset mode discovers tasks under tasks_dir when task_ids is omitted; pass IDs to select a subset.
Choose a mode with the constructor’s prewarm argument, or override it for one call with prewarm(..., prewarm="full") or prewarm_lifespan(..., prewarm="full"):
Progress logs identify each completed task, elapsed time, and any failure phase. Isolated task failures let the server start with those tasks unavailable; rollouts selecting them fail with the recorded reason. Widespread failures or repeated authentication, proxy, or quota errors abort startup. Inspect
backend.prewarm_summary and backend.failed_prewarm_tasks for the outcome.
For OpenSandbox, a positive OPENSANDBOX_MAX_SANDBOXES limits prewarm concurrency to half that value, rounded down with a minimum of one. The Platform can supply this limit in managed runs. Prewarm uses the Harbor queue, separately from the rollout trial limit. With no explicit queue, its capacity is at least 8 even when the rollout limit is lower.
Trial Limit
The trial limit is the number of Harbor trials one rollout server runs at once. Starting with SDK0.3.11, orchestrator is optional and HarborBackend resolves the limit as follows:
Omit
orchestrator so Platform runs use their limit. If you pass a TrialQueue with a lower n_concurrent, the rollout server runs fewer trials than the caller sends. The server logs a startup warning when that happens. A limit below 1 stops the server from starting.
At startup, the rollout server logs one line with the trial limit and its source, for example Trial limit: 32 at once (set by the platform for this run). The server’s /health response reports the limit as concurrency.max_concurrent. Local osmosis eval run caps in-flight rollouts at the smaller of that value and max_queue_depth.
Queue Depth and Cancellation
max_queue_depth limits waiting rollouts so the server can return HTTP 429 instead of growing an unbounded queue. rollout_status() reports queued, running, grading, or recently finished state; cancel_rollouts() cancels queued or running work by ID, prefix, or all.
rollout_status() remains available for backend-local diagnostics, but server result polling reads progress from the active RolloutContext. Custom backends must await RolloutContext.set_status() to publish status; callers can use RolloutClient to observe progress and cancel one rollout.
If you pass a TrialQueue, keep its default RetryConfig(max_retries=0). Queue-level attempt retries are not supported by the backend’s terminal-event contract; resubmit with a new rollout ID instead.
Server Shutdown
From0.3.6, stopping a rollout server with SIGTERM invokes backend cleanup automatically:
- The server closes rollout admissions and allows active work 10 seconds to finish.
- It requests cancellation and allows up to 120 seconds for cleanup, then directly cancels remaining rollout tasks and waits up to 10 more seconds.
HarborBackend.shutdown()cancels remaining trials and waits up to 180 additional seconds for cleanup, including artifact archiving.
delete setting. If cleanup times out, the backend reports the number of unfinished rollouts to stdout. This is not a count of remaining sandboxes or confirmation that deletion succeeded.
Budget up to 320 seconds for these SDK cleanup stages, plus time for Uvicorn to finish in-flight HTTP requests before app shutdown and for process shutdown overhead. Set the host’s termination grace period to cover that total; 320 seconds is not a bound on the entire shutdown. Forced termination can interrupt cleanup; Daytona’s auto-stop policy remains a separate backstop when enabled.
Managed and Self-Hosted Environments
Choose the Harbor environment explicitly withenvironment_config. The examples above use OpenSandbox, which starts each trial from a prebuilt image as described below. Select Daytona for tasks that Harbor should build from a Dockerfile. Omitting environment_config keeps Harbor’s Docker default and does not select a managed sandbox automatically.
Managed evaluation, training, and dev server runs receive Platform-managed OpenSandbox and Daytona credentials, so a rollout that selects either environment needs no sandbox secret. The managed OpenSandbox credential is issued per run: it reaches only the sandboxes that run creates and stops working when the run ends.
Harbor builds the selected task’s Dockerfile for Daytona; you do not manually build or push an image, configure registry credentials, or select a cluster.
To use your own Daytona account for managed runs, add DAYTONA_API_KEY to [secrets].required in both the evaluation and training configs, keeping any other required names. Store the credential with:
osmosis eval run, supply DAYTONA_API_KEY through the process environment or --secrets-file; a Platform secret record alone does not make it available locally. Never put secret values in the TOML config.
osmosis eval run accepts every Harbor environment for local eval and keeps the entrypoint’s usual environment_config. OpenSandbox, Daytona, and Docker outside macOS cannot reach your machine’s loopback interface. For these environments, the CLI automatically starts a managed cloudflared tunnel so the sandbox can reach the local model bridge. To use your own tunnel instead, pass it with --advertise-url. See eval run.
EnvironmentType.DOCKER also remains useful in a self-hosted SDK harness with a Docker daemon, but the managed rollout server does not provide that daemon. A self-hosted harness can select EnvironmentType.DAYTONA as well; the harbor extra already carries its dependencies, so only Daytona credentials are additionally required.
OpenSandbox Routing and Prebuilt Images
Select OpenSandbox with:import_path=None), SDK 0.3.7 applies these defaults:
SDK
0.3.9 sets request_timeout_sec from OPENSANDBOX_REQUEST_TIMEOUT_SEC when that value is a positive whole number of seconds, and otherwise keeps Harbor’s default.
Explicit kwargs take precedence, and the SDK leaves the config object you pass in unchanged. Custom import_path environments receive no routing defaults. Platform runs configured with managed OpenSandbox receive their sandbox credentials automatically; do not add those managed credentials to [secrets].required. For local evaluation or a self-hosted server, export your OpenSandbox server’s OPENSANDBOX_DOMAIN and OPENSANDBOX_API_KEY in the process environment. The CLI’s ambient-credential redaction of logs.txt does not cover OPENSANDBOX_API_KEY, so review logs before uploading a local run.
OpenSandbox requires a prebuilt docker_image under [environment] in each task’s task.toml. It does not build environment/Dockerfile or run Docker Compose tasks. For example, a Python-only task can start from:
0.3.7, tasks without a Dockerfile skip patching automatically; explicitly setting patch_dockerfile_with_sdk=True logs one warning per task instead of failing.
Without a preinstalled SDK environment, a bundled workflow or Osmosis grader installs the wheel and its dependencies into the image’s system Python. The image needs Python 3.12 or later and working uv or python3 -m pip. To preinstall dependencies, build an image with a static uv at /opt/osmosis/uv, a Python 3.12 virtual environment at /opt/osmosis/venv, and all bundle requirements installed there. When that virtual environment exists, trials use /opt/osmosis/uv to install only the bundle wheel with --no-deps. Both the binary and preinstalled dependencies are required. Build managed-run images for linux/amd64.
Daytona Sandbox Lifecycle Defaults
In0.3.3, built-in Daytona environments (type="daytona", import_path=None, and delete=True) default to auto_stop_interval_mins=60 and auto_delete_interval_mins=0. Daytona stops a sandbox after 60 minutes of provider-observed inactivity and deletes it immediately after stopping. This is a cleanup backstop for a crashed rollout server; normal trial teardown and agent or grader timeouts still apply.
Activity means interactions recognized by Daytona, not CPU work inside the sandbox. Harbor command polling normally refreshes activity while the server drives a command. The policy does not check server health, and other clients’ activity can keep a sandbox alive after a crash.
For trials with longer periods without Daytona-visible activity, pass a larger interval in environment_config.kwargs, or set it to 0 to disable auto-stop:
HarborBackend(environment_config=environment_config, ...). The SDK preserves explicit lifecycle values and adds no defaults for delete=False, a custom import_path, or other providers. Disabling auto-stop leaves cleanup to normal teardown or your own cleanup policy.
Harbor 0.22 and 0.23 force immediate deletion for snapshot-backed Daytona sandboxes even with a nonzero deletion interval, and reject nonzero deletion intervals for GPU tasks. The default of
0 works with both paths.Next Steps
Execution Backend Overview
Compare LocalBackend, Harbor template mode, and Harbor dataset mode.
v0.3 Migration
Replace the legacy Harbor constructor and execution model.