- Published on
Anatomy of a Long-Running Agent: Session, Harness, and Sandbox
- Authors

- Name
- Jared Chung
Introduction
An agent that works for hours cannot depend on one process, one context window, or one temporary container staying alive. Processes crash. Context must be compacted. Workspaces become stale. Models and orchestration strategies change.
The durable part of the system is not the running agent loop. It is the record that allows work to be understood, resumed, and audited.
Managed agent systems described by OpenAI and Anthropic separate durable sessions from the harness and execution environment. Those attributed examples lead to a vendor-neutral architecture you can test locally.
Runtime model
Three Components, Three Lifecycles
A useful decomposition is:
selects context and actions
Durable session <------------------------------> Harness
| |
| records events | invokes tools
v v
Artifact store Sandbox(es)
files/processes
Session
The session is an append-only history of inputs, model outputs, tool requests, tool results, approvals, state transitions, and artifact references. It should survive process and sandbox loss.
Harness
The harness runs the control loop. It chooses which session events enter the model context, exposes tools, handles compaction, applies policy, routes work to subagents, and records new events. Harness logic should be replaceable because its assumptions can age as models improve.
Sandbox
The sandbox is where tools read files, execute processes, and create artifacts. It may be a local workspace, container, virtual machine, or remote environment. Treat it as replaceable unless the workload explicitly requires persistent machine state.
Putting all three into one container is convenient for a prototype. It also couples the durable record to the component most likely to be restarted or discarded.
Make the Session Authoritative
Use an append-only event schema with stable ordering and explicit relationships.
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Literal
@dataclass(frozen=True)
class SessionEvent:
event_id: int
session_id: str
event_type: Literal[
"user_input",
"model_output",
"tool_request",
"tool_result",
"approval",
"artifact",
"checkpoint",
]
created_at: datetime
payload: dict[str, Any]
parent_event_id: int | None = None
The model's context is a projection of this record. It may include the latest turns, a summary of older work, selected tool results, and current task state. The projection can be rebuilt; the event history should not be overwritten by the summary.
def build_context(events, token_budget):
current_goal = latest_event(events, "user_input")
checkpoint = latest_event(events, "checkpoint")
recent = take_recent_events(events, token_budget * 0.6)
evidence = select_relevant_artifacts(events, token_budget * 0.2)
return [current_goal, checkpoint, *evidence, *recent]
Store large files and command outputs as artifacts, then keep hashes, metadata, and references in the session. This avoids forcing every byte into the context window while preserving evidence.
OpenAI's beta Agents API exposes this separation directly. The Python example below creates a durable session with no execution environment, allowing the application to handle required tool actions itself. Keep beta-specific code isolated behind an adapter.
import os
from openai import OpenAI
client = OpenAI()
session = client.beta.agents.sessions.create(
agent={
"name": "repository-reviewer",
"model": os.environ["OPENAI_AGENT_MODEL"],
"instructions": (
"Inspect the supplied repository evidence, identify correctness risks, "
"and cite the file or test behind every finding."
),
"multi_agent": {
"enabled": False,
"max_concurrent_subagents": 1,
},
},
environment={"type": "none"},
input="Review the parser change and report any untested edge cases.",
metadata={"workflow": "parser-review-v1"},
)
print(session.id, session.status)
A session may later require the application to perform a function call. Submit the result as a new session event with an idempotency key so a client retry does not create duplicate input events:
client.beta.agents.sessions.events.create(
session_id=session.id,
events=[
{
"type": "agent.session.input.message",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "The test artifact is available at artifact://parser-tests.",
}
],
}
],
}
],
idempotency_key="parser-review-evidence-v1",
)
This SDK surface manages OpenAI's session representation. Your application still owns its acceptance criteria, tool-side effects, data policy, and any state outside the managed session.
Treat the Sandbox as Replaceable
Suppose an agent changes a repository, runs tests, and then loses its container. Recovery needs more than the conversation:
- a known repository revision or snapshot;
- declared modified artifacts;
- environment and dependency metadata;
- the last verified checkpoint;
- incomplete operations marked visibly.
A replacement sandbox should restore only declared state. Hidden state—an untracked file, a background process, or a package installed manually—should cause a visible recovery failure instead of silently changing behavior.
checkpoint:
repository_revision: 8b7d2f1
patch_artifact: artifacts/step-04.patch
runtime_image: agent-python-v1
completed_steps:
- reproduce_failure
- add_regression_test
next_step: implement_fix
verification:
command: uv run pytest tests/test_parser.py -q
status: failing_as_expected
This structure also supports multiple sandboxes. A research subagent may use a network-enabled environment while an implementation agent works in a restricted repository workspace. The harness chooses where a tool runs; the session records what happened.
Recover Without Repeating Unsafe Work
Session recovery and side-effect recovery are different. The session may show that an agent requested a ticket creation but never received the result. It cannot prove whether the external service committed the ticket before the connection failed.
Classify operations:
| Operation | Recovery approach |
|---|---|
| Read-only lookup | Repeat when safe |
| Local deterministic computation | Recompute or restore cached artifact |
| File mutation in version control | Inspect repository state and patch |
| External idempotent write | Retry with the same operation key |
| External write with queryable status | Reconcile before retrying |
| Irreversible unknown write | Stop for review |
Bind approvals to a specific payload or operation hash. Resuming a session must not turn approval for one action into approval for a broader request.
def approval_matches(approval, operation):
return (
approval.operation_id == operation.id
and approval.payload_hash == operation.payload_hash
and approval.expires_at > now()
)
Exactly-once effects cannot be promised across arbitrary external systems. The architecture should make unknown outcomes explicit and route them to reconciliation or human review.
Test Failure and Replacement
A local architecture demo should inject failure rather than only show a successful run:
- Start a multi-step repository task.
- Persist every event outside the sandbox.
- Terminate the harness after a tool request.
- Replace the sandbox with a clean environment.
- Restore declared artifacts and rebuild context.
- Resume from the last verified checkpoint.
- Confirm that completed steps were not repeated.
- Repeat with an external outcome deliberately left unknown.
Record the event log, artifact hashes, model and harness versions, and expected transition at each point. A test should fail if an undeclared artifact is required for recovery.
Managed runtimes can remove substantial orchestration work, but they do not define your application contract, tool semantics, data boundaries, or evaluation criteria. Compare managed and self-hosted options on durability, environment placement, observability, policy, cost, and operational ownership.
Conclusion
Long-running agents become easier to reason about when session, harness, and sandbox are allowed to change on different schedules. Preserve an authoritative event history, make context a rebuildable view, declare artifacts, and assume execution environments will be replaced.
The resulting system can survive more than a long conversation. It can recover, change models and harnesses, use multiple environments, and still explain what happened.
References
- Introducing the Agents API, OpenAI, 10 September 2026
- Scaling Managed Agents: Decoupling the brain from the hands, Anthropic, 8 April 2026
- Harness design for long-running application development, Anthropic, 24 March 2026
- Create an agent session, official OpenAI API documentation
- Create agent session events, official OpenAI API documentation