SDK 0.14.6 Refresh — Discovery Report¶
Phase 0 outcome of the openai-agents 0.14 refresh effort. This report grounds all subsequent design in actual SDK API surface, not speculation. Captured against
openai-agents==0.14.6andopenai-chatkit==1.6.3in a throwaway venv at/tmp/af-discovery-014.
Verdict¶
PASS. openai-agents 0.14.6 is fully backward-compatible with AF as currently written. All 203 existing test cases pass against 0.14.6 with zero AF code changes.
Test Results¶
| Item | Value |
|---|---|
| Environment | /tmp/af-discovery-014/.venv (Python 3.12.9) |
agents version |
0.14.6 |
chatkit version |
1.6.3 |
| AF | editable install of this repo at v0.38-rc1 |
| Result | 203 / 203 PASSED in 175.98s |
| Log | /tmp/af-discovery-014/pytest-014.log |
| Discovery JSON | /tmp/af-discovery-014/discovery.json |
Real-LLM End-to-End Verification¶
tests/integration/test_sandbox_e2e.py (2 cases) exercises the full path AF → SDK → unix_local sandbox → host filesystem with gpt-5.5. Opt-in via AF_RUN_INTEGRATION=1; both cases PASS in ~11s when run.
| Case | What it proves |
|---|---|
test_sandbox_agent_writes_file_in_workspace |
The sandboxed agent uses shell access to create a file with the requested contents; the host observes it. |
test_sandbox_agent_reads_pre_seeded_file_round_trip |
A host-seeded file is readable from inside the sandbox and the agent can derive a new output file from it. |
These are gated to avoid surprise API cost; the default uv run pytest run deselects them via the e2e / integration marker filter (addopts in pyproject.toml). See Testing guide — Real-LLM End-to-End Tests for the opt-in commands.
Anchor Compatibility¶
Each surface in §3 Anchor of the refresh plan was verified against 0.14.6 actuals.
| Anchor | Status | Evidence |
|---|---|---|
Runner.run AF kwargs |
PASS | context, max_turns, run_config, session, previous_response_id, auto_previous_response_id, conversation_id all preserved. New error_handlers kwarg is additive. |
Runner.run_streamed.stream_events() / .final_output |
PASS | Test cases exercising both pass. |
Agent constructor passthrough |
PASS | All AF-passed kwargs accepted on 0.14.6 Agent. |
agents.memory.session.SessionABC inheritance |
PASS | Abstract methods unchanged: add_items, clear_session, get_items, pop_item. A new session_settings attribute exists on SessionABC but defaults to None and is NOT abstract — AF PhaseSession instantiates and operates correctly. |
agents.items.TResponseInputItem shape |
PASS | Union expanded to 28 arms (was 26); AF dict-shape checks unaffected. |
RunConfig passthrough |
PASS | All previously-used fields preserved; 9 new fields are additive. |
ChatKit run_streamed(..., context=AgentContext, ...) |
PASS | Indirectly verified via test_semantics::TestChatKit* cases. |
SDK Additions Since 0.6.9¶
Sandbox Agents (the headline feature)¶
- New module:
agents.sandbox(44 public symbols). - Main class:
agents.sandbox.SandboxAgent— subclass ofagents.Agent(MRO: SandboxAgent → Agent → AgentBase → Generic → object). - Adds 4 SandboxAgent-only constructor kwargs on top of
Agent:default_manifest,base_instructions,capabilities,run_as. - Everything else (tools, handoffs, model, model_settings, output_type, hooks, guardrails, …) is inherited.
- Run-time configuration:
agents.sandbox.SandboxRunConfig— standalone dataclass (not a subclass ofRunConfig). - Passing mechanism:
RunConfig(sandbox=SandboxRunConfig(...))—RunConfiggained a newsandbox: SandboxRunConfig | Nonefield. - Workspace declaration:
agents.sandbox.Manifest(Pydantic model). Fields:version,root,entries,environment,users,groups,extra_path_grants,remote_mount_command_allowlist. - Snapshots:
LocalSnapshot,RemoteSnapshot,LocalSnapshotSpec,RemoteSnapshotSpec,SnapshotSpec. - Memory:
MemoryGenerateConfig,MemoryLayoutConfig,MemoryReadConfig. - Built-in sandbox providers:
agents.sandbox.sandboxes.docker,agents.sandbox.sandboxes.unix_local. (Modal is not bundled in this version.) - Errors:
SandboxError,ExecTimeoutError,ExecTransportError,ExposedPortUnavailableError,WorkspaceArchiveReadError,WorkspaceArchiveWriteError,WorkspaceReadNotFoundError,WorkspaceWriteTypeError.
New Session Backends¶
agents.extensions.memory (empty in 0.6.9) now contains:
advanced_sqlite_session.pyasync_sqlite_session.pydapr_session.pyencrypt_session.pymongodb_session.pyredis_session.pysqlalchemy_session.py
Each is imported from its submodule, e.g. from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession. All satisfy SessionABC, so they slot into af.Runner(session=...) with no AF code changes.
Other Additive Changes¶
agents.memorygainedOpenAIConversationsSession,OpenAIResponsesCompactionSession,OpenAIResponsesCompactionAwareSession,SessionInputCallback,SessionSettings.Runner.run/run_streamedacceptRunState[TContext]as input (in addition tostr | list[TResponseInputItem]).Runner.run/run_streamedgainederror_handlers: RunErrorHandlers[TContext] | None.- 9 new additive
RunConfigfields:call_model_input_filter,handoff_history_mapper,nest_handoff_history,reasoning_item_id_policy,sandbox,session_input_callback,session_settings,tool_error_formatter,tracing.
No removals or signature breaks against AF's anchor surface.
Implications for AF Design¶
The original refresh draft had speculative shapes for af.SandboxAgent, af.Workspace, af.SandboxMemory. With the actual SDK surface in hand, the AF additions can be much smaller.
What AF Should Add¶
af.SandboxAgent— wrapsagents.sandbox.SandboxAgent. Returns the sameExecutionSpec[T]asaf.Agent. Constructor accepts the SandboxAgent-only kwargs (default_manifest,capabilities,run_as,base_instructions) on top of allaf.Agentkwargs.
That is the only required new symbol. pyproject.toml upper-bound bump (openai-agents>=0.14.0,<0.15) and a docs page complete the AF-side delta.
What AF Should NOT Add¶
- No
af.Workspace— the SDK already calls itManifest(Pydantic, stable). Wrapping adds no value; usersfrom agents.sandbox import Manifest. - No
af.SandboxMemory—MemoryGenerateConfig/MemoryLayoutConfig/MemoryReadConfigare SDK dataclasses. Pass through. - No new modifiers (
.checkpoint(),.workspace(), etc.) — sandbox runtime config travels viaRunConfig.sandbox, which threads through the existing.run_config(...)modifier. - No new state-management surfaces for
previous_response_id/conversation_id— already accessible via.run_kwarg(...)if a user needs them.
Confirmed Experience C (Sandbox)¶
import agentic_flow as af
from agents import RunConfig
from agents.sandbox import SandboxRunConfig, Manifest
coder = af.SandboxAgent(
name="coder",
instructions="Implement the spec in Python.",
model="gpt-5.5",
default_manifest=Manifest(
version="1",
root="/work",
entries=[...],
),
)
async def implement_flow(spec: str) -> str:
async with af.phase("Plan"):
plan = await planner(spec).stream()
async with af.phase("Implement", persist=True):
return await coder(plan).run_config(
RunConfig(sandbox=SandboxRunConfig(manifest=..., concurrency_limits=...))
).stream()
All existing modifiers (.stream(), .silent(), .snapshot(), .isolated(), .max_turns(), .context(), .run_config(), .run_kwarg()) work on af.SandboxAgent exactly as on af.Agent.
Confirmed Experience B (New Session Backends)¶
Zero AF code changes. User code:
from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession
session = SQLAlchemySession.from_url(session_id="thread-42", url="postgresql://...")
runner = af.Runner(flow=flow, session=session)
Deliverable is documentation only.
Plan Adjustments¶
| Plan section | Before discovery | After discovery |
|---|---|---|
| §6 Experience C sketch | af.Workspace, af.SandboxMemory proposed |
Use SDK Manifest / memory configs directly via RunConfig.sandbox |
| §8 File impact | "src/agentic_flow/sandbox.py — 形は Discovery 依存" | One file with one wrapper class |
| §6 Experience A | "Discovery で fail が出た場合のみ最小修正" | Confirmed: zero AF code changes |
| Phase 2 design effort | Open-ended | Reduced to a single wrapper class + tests + docs |
| §12 risk: "SandboxAgent の正確なクラス名・コンストラクタ署名は未検証" | Open | Resolved (documented above) |
Outcome¶
The plan above was executed in full and integrated into develop as commit 8cb30a3 (feat: port openai-agents 0.14 refresh to develop). Concretely:
src/agentic_flow/sandbox.pyand the four new docs/test files landed as planned.tests/test_sandbox.pycarries the 26 structural cases; the optionaltests/test_session_backends.pywas implemented with 9 cases (skipped viaimportorskipwhensqlalchemy/aiosqliteare absent).- A separate marker-gated layer was added:
tests/integration/test_sandbox_e2e.py(2 cases, opt-in viaAF_RUN_INTEGRATION=1) covers the realunix_localsandbox + LLM round-trip. - The
docs/en/guides/sessions.mdpage was not created; the same information lives indocs/en/concepts/sandbox.md(## Verification) anddocs/en/guides/testing.md(## Real-LLM End-to-End Tests). - The
feature/sdk-0.14-refreshbranch was retired after the squash merge;developis the integration branch.
Follow-up: Runner-injected default_run_config¶
The original 0.14 refresh deliberately stayed within the SDK pass-through line: "no new modifiers, sandbox runtime config travels via RunConfig.sandbox through the existing .run_config() modifier." That decision is correct in the small (no new AF abstraction over SDK types) but it left a tension with AF's own Flow/Runner separation principle (see docs/en/concepts/flow-runner.md): Session and Handler are injected by the Runner so that the flow body stays free of execution-environment concerns, yet RunConfig(sandbox=SandboxRunConfig(...)) still had to appear inline in the flow body on every sandboxed call.
Runner.default_run_config (added after 0.14 stabilized) closes that gap:
af.Runner(default_run_config=RunConfig(...))now sets acurrent_run_configcontextvar for the lifetime of the flow execution, in the same waycurrent_sessionandcurrent_handlerare set.ExecutionSpec.build_run_kwargs()resolvesrun_configwith the priority.run_config()modifier >current_run_configcontextvar > SDK default.- No new modifier was added; the
.run_config()chain still works exactly as before for per-call overrides.
This is purely additive: existing flows that pass RunConfig per call continue to behave identically. The path through the SDK is unchanged — RunConfig is still the SDK type, and AF still does not wrap it.
The injection now reaches the ChatKit path as well. af.chatkit.run_with_chatkit_context delegates flow execution to Runner.__call__, so Runner(default_run_config=RunConfig(sandbox=SandboxRunConfig(...))) works identically through ChatKit, including for af.SandboxAgent. The 0.14 gap is fully closed.
Net effect on the canonical sandbox example: the flow body shrinks back to pure business logic, and the sandbox transport (SandboxRunConfig) sits next to Session on the Runner where execution-environment configuration belongs.