opsen
opsen

Temporal

Durable agent workflows, with opsen as the sandbox — and no opsen code to write.

Temporal's OpenAI Agents integration takes any BaseSandboxClient. openai-agents-opsen is one, so it registers directly — there is no Temporal-specific opsen package to install.

pip install openai-agents-opsen 'temporalio[openai-agents]'

On the worker

from temporalio.contrib.openai_agents import (
    OpenAIAgentsPlugin,
    SandboxClientProvider,
)
from openai_agents_opsen import OpsenSandboxClient

plugin = OpenAIAgentsPlugin(
    sandbox_clients=[
        SandboxClientProvider("opsen", OpsenSandboxClient(
            task_id="nightly-report",
            budget_usd=2.00,
        )),
    ],
)

Each provider gets its own set of Temporal activities, prefixed with its name, so several sandbox backends can share one task queue. Registering opsen alongside another provider is supported and occasionally useful — run the cheap steps one place and the metered ones here.

In the workflow

from agents import RunConfig
from agents.sandbox import SandboxRunConfig
from temporalio.contrib.openai_agents.workflow import temporal_sandbox_client

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=temporal_sandbox_client("opsen"),
    ),
)

The workflow refers to the provider by name. It holds no credentials and no client object, which is what makes the workflow deterministic and replayable.

Why this combination is worth having

Temporal exists because long agent runs fail halfway. But a retried workflow re-runs steps, and every re-run spends money again — a durable execution layer makes the failure survivable and makes the cost worse.

A budget_usd on the opsen client is a ceiling on the whole thing rather than on one attempt, because the cap belongs to the session and the session is what the activities share. A workflow that retries into a loop is refused rather than allowed to bill for each attempt.

Temporal marks SandboxClientProvider experimental. It has been stable in practice, but pin your temporalio version rather than tracking latest.

Costs, per workflow

opsen.task_cost("nightly-report")

Use the workflow id as the task_id and every activity, every retry and every model call it made add up under one number.