# Diagrams — DD-11: OpenAI Agents SDK (2-Layer Harness/Compute Split)

**Course**: Master Course · **Deep-Dive**: DD-11
**Subject**: SDK / primitives library — 2-layer harness/compute split, 7 sandbox providers, handoffs + agents-as-tools
**Diagram count**: 6

---

## Diagram 1 — The 2-Layer Harness/Compute Split

```mermaid
flowchart TB
    subgraph HARNESS["HARNESS LAYER  (your backend)"]
        direction TB
        LOOP["loop + model calls + orchestration"]
        CRED["credentials held HERE"]
        HO["handoff decisions"]
    end
    subgraph COMPUTE["COMPUTE LAYER  (sandbox)"]
        direction TB
        EXEC["tool execution ONLY"]
        PROVIDERS["7 providers: local · Docker · E2B · Modal · Daytona · Cloudflare · Vercel"]
    end
    HARNESS -->|"API call (no credentials cross)"| COMPUTE
    COMPUTE -->|"result returned"| HARNESS
    CRED -.->|"NEVER enters sandbox — structural impossibility"| PROVIDERS
    style HARNESS fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style CRED fill:#152d15,stroke:#208a20,color:#a0d0a0
    style COMPUTE fill:#14141f,stroke:#1e8449,color:#82e0aa
    style PROVIDERS fill:#0d2818,stroke:#1e8449,color:#82e0aa
```

- **Type**: Architecture / reference.
- **Purpose**: The defining diagram. Establishes that the harness layer (loop, model calls, credentials) and the compute layer (tool execution in a sandbox) live in different places, and that the SDK *prevents* credentials from crossing the boundary. This is the structural argument for why credential isolation is architectural rather than optional.
- **Reading**: The harness layer runs on your backend and holds credentials. The compute layer is a sandbox managed by one of 7 providers. The dashed line is the key: there is no code path in the SDK that lets credentials enter the sandbox. Contrast with a single-layer harness where the loop and the tool execution share an address space and credentials are, by default, visible to the model. The 2-layer split converts a discipline you maintain into a property you inherit. Same principle as NemoClaw (DD-09), applied to a different trust boundary — credentials outside the sandbox's reach vs governance outside the agent's reach.

---

## Diagram 2 — The 7-Provider Sandbox Abstraction (Module 5 Reference)

```mermaid
flowchart TB
    CODE["your tool code (written ONCE)"]
    CODE --> ABSTRACT["SandboxProvider interface  (SDK abstraction)"]
    ABSTRACT --> LOCAL["local bash  (dev, no isolation)"]
    ABSTRACT --> DOCKER["Docker  (local/CI, container)"]
    ABSTRACT --> E2B["E2B  (cloud, ephemeral microVM)"]
    ABSTRACT --> MODAL["Modal  (serverless cloud)"]
    ABSTRACT --> DAYTONA["Daytona  (dev env provisioning)"]
    ABSTRACT --> CF["Cloudflare  (edge-proximate)"]
    ABSTRACT --> VERCEL["Vercel  (serverless, deploy-integrated)"]
    style CODE fill:#14141f,stroke:#5eead4,color:#e4e4e8
    style ABSTRACT fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style LOCAL fill:#2a1810,stroke:#a04000,color:#f0a868
    style DOCKER fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style E2B fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style MODAL fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style DAYTONA fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style CF fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style VERCEL fill:#0d2818,stroke:#1e8449,color:#82e0aa
```

- **Type**: Architecture / reference.
- **Purpose**: The Module 5 reference. Shows that a single tool, written once, can target 7 isolation regimes through a uniform abstraction — the swap is configuration, not a code change. This is the load-bearing reason the SDK scores 5/5 on Module 5 (Sandboxing), the highest in the roster.
- **Reading**: Local bash (warn-colored) is the development provider — no isolation, fast iteration. The other six are isolation regimes you pick at deployment time. The architectural payoff is that the harness code is provider-agnostic: the same loop, the same tool, the same handoff logic runs in any regime. Compare to OpenCode (DD-03), where the client/server split enables sandboxing but the sandbox is Docker-or-nothing. The SDK's abstraction means a misconfigured weak regime (local bash in production) is a config error caught in review, not an architectural mistake discovered after deployment.

---

## Diagram 3 — Handoffs vs Agents-as-Tools (Both First-Class — Module 1.3)

```mermaid
flowchart LR
    subgraph AAT["AGENTS-AS-TOOLS  (delegation by query)"]
        direction TB
        PARENT1["Parent agent"]
        PARENT1 -->|"invokes like a tool, synchronous"| SUB1["Subagent"]
        SUB1 -->|"returns structured result"| PARENT1
        PARENT1 -->|"retains control, continues"| END1["task complete"]
    end
    subgraph HO["HANDOFFS  (delegation by transfer)"]
        direction TB
        PARENT2["Parent agent"]
        PARENT2 -->|"transfers control, TERMINAL"| SUB2["Subagent"]
        SUB2 -->|"owns task to completion"| END2["task complete"]
    end
    style AAT fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style HO fill:#2a1810,stroke:#a04000,color:#f0a868
```

- **Type**: Pattern / comparison.
- **Purpose**: Distinguishes the two Module 1.3 subagent patterns the SDK formalizes. Shows that the SDK is the only framework in the roster that offers both as first-class primitives rather than hand-rolled. This is the load-bearing reason for the 4/5 on Module 10 (Subagents).
- **Reading**: Agents-as-tools (teal) — the parent retains control. The subagent is a capability the parent invokes, gets a structured result, and continues orchestrating. Use this for query-style delegation where the parent should keep visibility (ask the DB agent a question, continue). Handoffs (warn) — the parent transfers control. The handoff is terminal; the subagent owns the task to completion and the parent stops. Use this for genuine transfer (the DB agent takes over the whole task). The SDK offers both because different tasks need different control models — most frameworks implement one or neither.

---

## Diagram 4 — Declarative-by-Composition (the orchestration model)

```mermaid
flowchart TB
    DEV["YOU define agents"]
    DEV --> A1["Agent A: model, instructions, tools, handoff targets"]
    DEV --> A2["Agent B: model, instructions, tools, handoff targets"]
    DEV --> A3["Agent C: model, instructions, tools, handoff targets"]
    A1 -.->|"handoff to B"| A2
    A2 -.->|"agents-as-tools call to C"| A3
    A1 & A2 & A3 --> RUNTIME["SDK RUNTIME drives the loop"]
    RUNTIME --> LOOP["loop + handoff routing + sandbox dispatch"]
    LOOP --> RESULT["task complete"]
    style DEV fill:#14141f,stroke:#5eead4,color:#e4e4e8
    style RUNTIME fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style LOOP fill:#0d2818,stroke:#1e8449,color:#82e0aa
```

- **Type**: Architecture / process.
- **Purpose**: Shows the orchestration model — you define agents, the SDK runtime drives the loop. You do not write the loop; you configure the agents and the SDK runs the loop for you. This positions the SDK between LangGraph (DD-10, fully-declared-graph) and Pi (DD-01, hand-written-loop).
- **Reading**: The top half (teal-bordered) is what you as the developer write: agent definitions, each declaring a model, instructions, tools, and handoff targets. The dashed lines are the delegation relationships you declare (handoffs or agents-as-tools). The bottom half (green) is what the SDK runtime does: it drives the loop, routes handoffs, and dispatches to sandboxes. You never see the loop code. This is the "declarative-by-composition" model — structure without forcing you to declare every edge, contrasted with LangGraph's "declare the whole graph up front" model.

---

## Diagram 5 — The Same-Principle-Different-Layer Connection (NemoClaw DD-09)

```mermaid
flowchart LR
    subgraph NEMO["NEMOCLAW (DD-09)"]
        direction TB
        AGENT["the agent"]
        GOV["governance gate"]
        AGENT -->|"proposes action"| GOV
        GOV -->|"enforces policy"| ACTION["action executes"]
        GOV -.->|"gate is OUTSIDE the agent's reach"| AGENT
    end
    subgraph SDK["OPENAI AGENTS SDK (DD-11)"]
        direction TB
        SANDBOX["the sandbox"]
        CRED["credentials"]
        SANDBOX -->|"executes tools"| TOOLS["tool runs"]
        CRED -.->|"credentials are OUTSIDE the sandbox's reach"| SANDBOX
    end
    style NEMO fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style GOV fill:#152d15,stroke:#208a20,color:#a0d0a0
    style SDK fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style CRED fill:#152d15,stroke:#208a20,color:#a0d0a0
```

- **Type**: Conceptual / cross-reference.
- **Purpose**: The load-bearing connection. Shows that NemoClaw (DD-09) and the OpenAI Agents SDK (DD-11) apply the *same* architectural principle — enforcement outside the principal's reach — to *different* trust boundaries. NemoClaw enforces governance outside the agent's reach; the SDK enforces credential isolation outside the sandbox's reach.
- **Reading**: In NemoClaw, the governance gate sits between the agent's proposal and the action that executes. The agent cannot bypass the gate because the gate is not in the agent's address space — it is in the harness layer, beneath the agent. In the Agents SDK, the credential boundary sits between the sandbox and the credentials. The sandbox cannot reach the credentials because the credentials live in the harness layer, outside the sandbox's address space. Same principle, different layer. Both convert a discipline you maintain (don't bypass the gate; don't leak credentials) into a property you inherit (the gate is structurally unreachable; the credentials are structurally inaccessible). This is the architectural-pattern payoff the course wants you to internalize.

---

## Diagram 6 — 2-Layer Sandbox Dispatch (n8n state-machine workflow)

```json
{
  "name": "OpenAI Agents SDK 2-Layer — DD-11",
  "nodes": [
    { "parameters": { "assignments": { "assignments": [{ "id": "task", "name": "task", "value": "read auth.ts and refactor", "type": "string" }, { "id": "credentials", "name": "model_api_key", "value": "sk-NEVER-IN-SANDBOX", "type": "string" }]}, "options": {} }, "id": "harness", "name": "Harness Layer (backend)", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [160, 300], "notesInFlow": true, "notes": "Loop + model calls + orchestration. Credentials held HERE. Runs on YOUR backend. The SDK runtime drives the loop." },
    { "parameters": { "assignments": { "assignments": [{ "id": "prov", "name": "sandbox_provider", "value": "={{ ({local:'bash', docker:'container', e2b:'microVM', modal:'cloud-container', daytona:'dev-env', cloudflare:'edge', vercel:'edge'})[$json.body.provider || 'docker'] }}", "type": "string" }]}, "options": {} }, "id": "provider", "name": "Sandbox Provider (7 options, config-swappable)", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [400, 300], "notesInFlow": true, "notes": "7 providers behind uniform abstraction. Swap by CONFIG, not code change. Harness is provider-agnostic. Module 5 reference (5/5)." },
    { "parameters": { "command": "={{ 'echo [compute layer] executing in ' + $json.sandbox_provider + ': reading auth.ts' }}", "executeOnce": true }, "id": "compute", "name": "Compute Layer (sandboxed execution)", "type": "n8n-nodes-base.executeCommand", "typeVersion": 1, "position": [640, 300], "notesInFlow": true, "notes": "Tool execution ONLY. NO credentials in env. The SDK architecture PREVENTS credentials from entering here — credential isolation is architectural (structural impossibility, not a policy)." },
    { "parameters": { "content": "={{ JSON.stringify({ result: 'executed in ' + $json.sandbox_provider, credentials: 'NEVER entered sandbox (2-layer split)', pattern: 'handoffs + agents-as-tools both first-class (Module 1.3)', score: '38/60 — Module 5 Sandboxing 5/5' }) }}", "options": {} }, "id": "out", "name": "Output", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1, "position": [880, 300] }
  ],
  "connections": { "Harness Layer (backend)": { "main": [ [ { "node": "Sandbox Provider (7 options, config-swappable)", "type": "main", "index": 0 } ] ] }, "Sandbox Provider (7 options, config-swappable)": { "main": [ [ { "node": "Compute Layer (sandboxed execution)", "type": "main", "index": 0 } ] ] }, "Compute Layer (sandboxed execution)": { "main": [ [ { "node": "Output", "type": "main", "index": 0 } ] ] } },
  "settings": { "executionOrder": "v1" }, "meta": { "templateCreatedBy": "Harness Engineering Master Course — DD-11 OpenAI Agents SDK" }
}
```

- **Type**: n8n state-machine workflow (importable).
- **Purpose**: An executable diagram you can import into n8n to see the 2-layer dispatch flow concretely. The harness node holds credentials; the provider node shows the 7-way config swap; the compute node confirms credentials never enter the sandbox.
- **Reading**: Import into n8n (or read top-to-bottom as a flow). The `Harness Layer` node assigns both the task and the model API key — note the key stays in the harness layer's state. The `Sandbox Provider` node demonstrates the config-swap expression: change the `provider` field in the webhook body and the sandbox regime changes without touching tool code. The `Compute Layer` node runs the tool and the response confirms the architectural properties: credentials never entered, both subagent patterns available, Module 5 at 5/5. This is the lab flow the `07-lab-spec.md` simulates in pure Python.
