DOC11_PROPOSAL_EXTERNAL_AGENT_ENGINE_ADAPTER.md
Current Specs/DOC11/DOC11_PROPOSAL_EXTERNAL_AGENT_ENGINE_ADAPTER.md
ELNOR REPO READER TEXT MIRROR
Original path: Current Specs/DOC11/DOC11_PROPOSAL_EXTERNAL_AGENT_ENGINE_ADAPTER.md
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z
---
# DOC11 — External Agent Engine Adapter (PROPOSAL)
**Status:** proposal — for architect review + red team; **not operative.** No existing file is modified by this document; it binds to other specs by reference only.
**Target owner doc:** DOC11 (OpenClaw Gateway-First runtime truth, model controls, usage telemetry, EC/Q integration).
**Compatibility:** operative **DOC11 R14** (`DOC11_OPENCLAW_GATEWAY_MODEL_CONTROLS_AND_EC_INTEGRATION_R14.md`, v1.11.30 R14) **and** pending **R15** (`DOC11_R15_OPENCLAW_RELEASE_ALIGNMENT_AMENDMENT_PROPOSAL_R3.md`). See §10.
**Primary source of truth (the brief):** `OP-A and Operations and Trackers/CAPABILITY_NOTE_EXTERNAL_AGENT_CLI_ADAPTERS.md` (architect-approved 2026-06-04; full headless loop verified 2026-06-04 on subscription auth).
**Date:** 2026-06-04 · **Architect:** Will Brody.
---
## 0. Scope, ownership boundary, and how to read this
### 0.1 What this capability is
ELNOR drives external coding/review engines **headlessly** — on the architect's **subscriptions**, not per-token API billing where avoidable — with engine routing, model/think controls, billing-path discipline, run provenance/telemetry, and EC policy gating. The capability is operationally proven (capability note §1: Codex `LOOP_CONFIRMED` 2026-06-04, no API key; Claude Code in daily headless use).
This is squarely DOC11's existing job: the adapter mostly **reports runtime truth about, and exposes controls over, engine/runtime machinery OpenClaw already provides** (provider/model/runtime selection, auth-source, billing path), plus an **EC-driven external-invocation bridge** for the arms-length (file-bus) case. It introduces no new truth store and no second prompt-control language.
### 0.2 Ownership boundary — what this proposal OWNS vs REFERENCES
| Concern | Owner | This proposal |
|---|---|---|
| Engine/agent registry, adapter/invocation contracts, runtime truth of engines | **DOC11 (this doc)** | defines |
| `ResolvedEnginePlan`, manual override, `routing_mode`, why-routed telemetry | **DOC11 (this doc)** | defines |
| Billing-path assertion + per-engine tier/billing rules + plan meters | **DOC11 (this doc)** | defines |
| Run provenance + usage telemetry integration | **DOC11 (this doc)** | defines, extends R14 telemetry |
| **Routing *policy* engine** (rules/signals/weights deciding *which* engine) | **separate future spec** (provisional `DOC-ROUTING`) | **referenced**, not defined (§3.5) |
| **Reverse MCP server** (ELNOR exposed to external LLMs = `mcp_pull`) | DOC16 §16.7.4 (config/scope) + DOC24 §17 (scope enforcement) + DOC11 runtime | **referenced**, not built here (§6.4) |
| Memory **selection/assembly** of injected context | DOC24 / DOC84 (delivery), DOC72/DOC73 | **referenced**; adapter consumes a rendered packet (§6.4) |
| **Egress** classification + delegation MFC | E0 §22 (`E0OutboundDestinationClass`) + §3.3 (`DelegationMFC`) | **referenced, never redefined** (§6.2) |
| **Send-time gate / disclosure policy** | DOC81 R2 (send-time gate; §4.0 internal-use-permissive; R-5 multi-principal) | **referenced, never redefined** (§6.2, §9) |
| **Credential providers, secret leases, account setup, action/egress gate** | **DOC5** (security/auth/credential plane, in planning) | **referenced**; adapter states hygiene inline now and **re-homes to DOC5** when it lands (§6.3) |
| Cost/billing-tier rows, quota/usage views | DOC13 | **obligation** (§8) |
| Engines settings panel render, auth-source indicator, credit meter UI | DOC20 | **obligation** (§8) |
| External agents as multi-agent room participants / ACP | DOC12 + OpenClaw ACP / Codex Supervisor | **obligation** (§8) |
| EC policy-evaluator socket + durable run/receipt writes | EC Core | **obligation** (§8) |
### 0.3 No-phantom statement
Every contract, field, lint, and fixture below traces to the capability note, DOC11 R14/R15, E0/DOC81, the live OpenClaw runtime docs, or Anthropic's published billing policy — or it is flagged `OPEN_FOR_ARCHITECT_REVIEW` in §11 (5 items, the cap). DOC11 R14 conventions are matched: branded `*_ref` types, `schema_owner`, capability-evidence labels (`native_probed | native_documented_not_probed | wrapper_composed | unsupported | advisory_only`, R14 §0.4), desired/effective/executed truth, settings-only honesty, and DOC10 XDI companion-ledger rows for every cross-doc obligation (R14 §0.7).
---
## 1. The model: three orthogonal axes, aligned to OpenClaw's real runtime model
The earlier "native backend vs external delegate" binary is replaced by the way OpenClaw actually works (live docs, `concepts/agent-runtimes`): a **provider × runtime** matrix, several rows of which are subscription-authed with no per-token fee, all OpenClaw-orchestrated. The adapter models **three orthogonal axes**:
1. **`engine`** — vendor/product family: `claude | openai_codex | gemini`.
2. **`invocation_topology`** — *who runs the loop and where*, mapped to OpenClaw runtime ids where applicable.
3. **`memory_access_mode`** — how the run reaches ELNOR memory: `native_push | mcp_pull | none`. (Context level, including "no context", is therefore a setting on any topology — decoupled from the topology choice.)
```ts
type EngineFamily = "claude" | "openai_codex" | "gemini";
type InvocationTopology =
| "openclaw_runtime_pi" // OpenClaw PI runner; e.g. Codex OAuth provider route (openai + ChatGPT/Codex OAuth). OpenClaw owns the full loop.
| "openclaw_runtime_codex" // OpenClaw bundled Codex app-server runtime (agentRuntime.id="codex"); Codex owns more of the loop, OpenClaw mirrors/projects.
| "openclaw_runtime_claude_cli"// OpenClaw claude-cli backend (agentRuntime.id="claude-cli"; claude -p). OpenClaw drives the local claude binary.
| "openclaw_acp" // OpenClaw ACP/acpx control plane for external harnesses (Claude Code, Gemini CLI, OpenCode, Cursor).
| "ec_external_cli" // EC drives an external CLI (codex exec / claude -p / gemini) directly, outside OpenClaw, via the file bus.
| "ec_external_sdk_embedded" // EC embeds an SDK in-process (Claude Agent SDK / Codex SDK) outside OpenClaw.
| "raw_api" // direct provider API (per-token).
| "local"; // local model.
type MemoryAccessMode = "native_push" | "mcp_pull" | "none";
type BillingTier = "T1_subscription" | "T2_api" | "T3_local"; // capability note §5
```
Reference map (engine × topology × billing), traceable to OpenClaw docs + capability note §5 + Anthropic policy (§4):
| Engine | Topology | Auth source | Billing | Loop owner | Evidence label (until probed) |
|---|---|---|---|---|---|
| openai_codex | `openclaw_runtime_pi` | ChatGPT/Codex OAuth | **T1 flat** | OpenClaw (PI) | `native_documented_not_probed` |
| openai_codex | `openclaw_runtime_codex` | ChatGPT/Codex OAuth | **T1 flat** | Codex app-server (mirrored) | `native_documented_not_probed` |
| openai_codex | `ec_external_cli` (`codex exec`) | ChatGPT/Codex OAuth | **T1 flat** | Codex | **`native_probed`** (LOOP_CONFIRMED 2026-06-04) |
| claude | `openclaw_runtime_claude_cli` (`claude -p`) | Claude subscription (CLI) | **T1 → Agent SDK credit pool** (see §4) | OpenClaw drives `claude` | `native_documented_not_probed` |
| claude | `ec_external_sdk_embedded` (Agent SDK) | Claude subscription / setup-token | **T1 → Agent SDK credit pool** | Claude Agent SDK | `wrapper_composed` (in daily use) |
| claude / openai_codex | `raw_api` | API key | **T2 per-token** | provider | `native_documented_not_probed` |
| any | `local` | — | **T3** | local | per existing local-first arch |
> Note (R14 §0.4 honesty): `openclaw_runtime_*` rows are labelled `native_documented_not_probed` because OpenClaw's docs document them as supported/common, but they have not been re-probed on the architect's current build since the April 2026 runtime migration. They flip to `native_probed` after the §11 OPEN-1 verification. They do not block this proposal; `ec_external_cli` (Codex) is proven.
---
## 2. Engine registry + adapter contracts
### 2.1 EngineDescriptor
```ts
type Brand<T, B extends string> = T & { readonly __brand: B };
type EngineDescriptorRef = Brand<string, "EngineDescriptorRef">;
type EngineRunRef = Brand<string, "EngineRunRef">;
type SessionRef = Brand<string, "SessionRef">;
type ReasonCodeId = Brand<string, "ReasonCodeId">;
interface EngineDescriptor {
engine_descriptor_ref: EngineDescriptorRef;
schema_owner: "DOC11";
engine: EngineFamily;
invocation_topology: InvocationTopology;
// version pinning + drift check (capability note §3 rule 5)
pinned_version: string; // e.g. codex-cli 0.137.0, claude-code 2.1.162, codex-sdk 0.137.0, agent-sdk 0.3.162
observed_version?: string; // probed at run time
version_drift_state: "match" | "drift_detected" | "unknown";
// auth + billing
auth_source: "subscription_oauth" | "subscription_cli" | "setup_token" | "api_key" | "local_none";
billing_tier: BillingTier;
expected_billing_path: "flat_subscription" | "agent_sdk_credit" | "per_token_api" | "local"; // asserted before every run (§4)
// runtime truth (R14 desired/effective/executed)
login_status: "logged_in" | "expired" | "reauth_required" | "missing" | "unknown";
health: "healthy" | "degraded" | "unavailable" | "unknown";
evidence_label: "native_probed" | "native_documented_not_probed" | "wrapper_composed" | "unsupported" | "advisory_only";
last_verified_at?: string;
user_visible_message?: string;
}
```
### 2.2 Invocation contracts (one-shot, resumed-session, embedded-SDK)
Maps to the capability note §1 interfaces and OpenClaw runtime selection.
```ts
type SandboxMode = "read_only" | "workspace_write_scoped" | "workspace_write_full"; // least-privilege default = read_only
interface EngineInvocation {
engine_run_ref: EngineRunRef;
schema_owner: "DOC11";
engine_descriptor_ref: EngineDescriptorRef;
mode: "one_shot" | "resumed_session" | "embedded_sdk_session";
// working directory + sandbox discipline (capability note §3 rule 6; least-privilege by role)
working_dir: string; // scoped to the task; report folder for write roles
sandbox_mode: SandboxMode;
permission_mode: string; // engine-native permission mode / allowlist gate (EC-consulted, §6.1)
tool_allowlist: string[];
// session continuity (capability note §1: codex exec resume; claude -p --resume; SDK session id)
session_ref?: SessionRef; // ELNOR<->engine session mapping; stamped on provenance
// memory
memory_access_mode: MemoryAccessMode; // native_push | mcp_pull | none (§6.4)
injected_packet_ref?: string; // when native_push: a DOC24/DOC84-rendered packet (reference; adapter does not assemble)
// the resolved plan a run consumes (§3)
resolved_plan_ref: string;
}
```
**Defaults (capability note §3 rule 6):** review/audit roles default `sandbox_mode = read_only`; report-writer roles get `workspace_write_scoped` to the report folder; `workspace_write_full` requires explicit per-run architect grant. All auth material is supplied to the engine process out-of-band, never via prompt/args that could be logged (§6.3).
### 2.3 Routes / read-models / events (R14 idiom)
```
POST /api/engines/run -> start an EngineInvocation, returns engine_run_ref
GET /api/engines/registry -> EngineDescriptor[] (runtime truth read-model)
GET /api/engines/run/{ref} -> AgentRunRecord (provenance + status, §5)
POST /api/engines/run/{ref}/abort -> STOP propagation (R14 abort/STOP seam)
```
Events (non-durable progress per R14 streaming rules; durable facts to EC §5):
`engine.run.started`, `engine.run.progress`, `engine.run.completed`, `engine.run.failed`, `engine.run.blocked`, `engine.billing_path_asserted`, `engine.credit_warning`.
---
## 3. Per-role routing **seam** (the policy engine is a separate spec)
### 3.1 Routing targets and named agents
A **named agent** is a saved bundle (engine / topology / model / think-level / memory-mode / permission / tool-allowlist) and is a first-class routing target. The adapter stores the bundle; *which* target a request routes to is a policy decision owned elsewhere (§3.5).
```ts
type ThinkLevel = "off" | "low" | "medium" | "high" | "max"; // thinking-token budget (capability note §6)
interface RoutingTargetBundle {
target_ref: string; // engine bundle or named agent
schema_owner: "DOC11";
display_label: string;
engine: EngineFamily;
invocation_topology: InvocationTopology;
model: string;
think_level: ThinkLevel;
permission_mode: string;
tool_allowlist: string[];
memory_access_mode: MemoryAccessMode;
billing_tier: BillingTier;
}
```
### 3.2 ResolvedEnginePlan — what a run consumes (DOC11 owns this)
```ts
interface ResolvedEnginePlan {
resolved_plan_ref: string;
schema_owner: "DOC11";
engine: EngineFamily;
invocation_topology: InvocationTopology;
model: string;
think_level: ThinkLevel;
permission_mode: string;
tool_allowlist: string[];
memory_access_mode: MemoryAccessMode;
billing_tier: BillingTier;
routing_mode: "manual" | "suggest" | "auto";
decided_by: "manual_override" | "named_agent" | "policy_rule" | "default";
why_routed_trace_ref?: string; // never a black box; rendered in Inspector
ec_policy_consulted: boolean; // routing is policy-respecting (§6.1)
}
```
### 3.3 Routing mode + override
`routing_mode` is scoped (global default / per-named-agent / per-conversation / per-run) and defaults to **`manual`** (or `suggest`). **Manual override always wins.** This honors the architect decision that automatic routing is deferred; the adapter ships the *seam* (inputs + recorded output), not an auto-router.
### 3.4 Lints
```
engine.run_without_resolved_plan
engine.named_agent_target_unknown
engine.routing_decision_without_trace // why-routed must exist when decided_by != manual_override
```
### 3.5 The routing **policy** engine is referenced, not defined here
The rules/signals/weights that *decide* a target (cost from DOC13, plan-window pressure, sensitivity from DOC81, subject tags, learned engine performance) belong to a separate spec (provisional `DOC-ROUTING`). DOC11 supplies the **inputs** (the registry + runtime truth) and records the **output** (`ResolvedEnginePlan` + why-routed). Cross-doc obligation in §8.
---
## 4. Billing-path discipline (the cost guarantee)
### 4.1 Per-engine billing model (traceable to OpenClaw docs + Anthropic published policy)
- **Codex / ChatGPT subscription** → **T1 flat.** OpenAI explicitly supports Codex OAuth in external tools like OpenClaw; no separate programmatic credit cap is surfaced. Default for heavy/long/bulk agentic runs.
- **Claude subscription** → **T1, but metered to the Agent SDK credit pool.** Per Anthropic's help center, starting **2026-06-15** the Agent SDK and `claude -p` no longer count toward interactive plan limits; they draw from a separate **monthly Agent SDK credit** (Pro $20 / Max 5x $100 / Max 20x $200), at standard API rates, no rollover; past the credit, usage flows to API rates only if usage credits are enabled, else requests stop. `claude -p` (headless) is classified as programmatic regardless of being "the CLI"; the interactive subscription bucket has no automation entry point. Therefore **all adapter-driven Claude use is the credit pool, then API.**
- **API key (either vendor)** → **T2 per-token** (explicit overflow).
- **Local** → **T3.**
### 4.2 Billing-path assertion before every run
```ts
interface BillingPathAssertion {
engine_run_ref: EngineRunRef;
schema_owner: "DOC11";
expected_billing_path: "flat_subscription" | "agent_sdk_credit" | "per_token_api" | "local";
asserted_env_clean_of_api_key: boolean; // the ANTHROPIC_API_KEY-silently-wins trap (capability note §5 rule 1)
assertion_result: "pass" | "mismatch_blocked";
reason_codes: ReasonCodeId[];
}
```
The `ANTHROPIC_API_KEY` (and equivalent) silently overrides subscription OAuth, so API keys stay **out of the default agent environment**; the adapter asserts the expected billing path **before every run** and blocks on mismatch. This assertion + no-silent-spill is the mechanism that actually guarantees "use the subscription, save API fees."
### 4.3 Queueing + spill
Subscription plans rate-limit (capability note §5 rule 2): the adapter **queues (concurrency 1–2)** and surfaces stalls rather than spinning. **Spill to T2 (raw API) only via an explicit policy toggle with a recorded reason** — never silently.
### 4.4 Claude Agent SDK credit meter
```ts
interface ClaudeCreditMeter {
schema_owner: "DOC11";
plan_tier: "pro" | "max_5x" | "max_20x" | "unknown"; // OPEN-4: confirm tier for defaults
monthly_credit_usd: number; // 20 / 100 / 200
consumed_usd_estimate: number;
remaining_usd_estimate: number;
reset_at: string; // refreshes with billing cycle, no rollover
on_exhaustion: "block" | "spill_to_api_if_enabled"; // OPEN-4 behavior choice; default block
warn_threshold_pct: number; // surfaced in DOC20 Engines panel + warning event
}
```
### 4.5 Lints
```
engine.billing_path_mismatch // expected != actual (capability note build lint)
engine.api_key_in_default_agent_env // API key present where subscription OAuth expected
engine.silent_api_spill // T1 -> T2 without explicit toggle + reason
engine.claude_run_without_credit_meter // Claude T1 run not metered against the Agent SDK credit
```
---
## 5. Run provenance + telemetry
```ts
interface AgentRunRecord {
engine_run_ref: EngineRunRef;
schema_owner: "DOC11";
engine: EngineFamily;
invocation_topology: InvocationTopology;
model: string;
think_level: ThinkLevel;
billing_tier: BillingTier;
billing_path_actual: "flat_subscription" | "agent_sdk_credit" | "per_token_api" | "local";
auth_source: EngineDescriptor["auth_source"];
sandbox_mode: SandboxMode;
session_ref?: SessionRef;
memory_access_mode: MemoryAccessMode;
credit_consumed_usd_estimate?: number; // for Claude agent_sdk_credit runs
evidence_label: EngineDescriptor["evidence_label"];
started_at: string;
ended_at?: string;
outcome: "success" | "blocked" | "failed" | "degraded" | "requires_user_action";
report_artifact_ref?: string; // file-bus report (§6.5)
reason_codes: ReasonCodeId[];
}
```
Provenance integrates with DOC11 R14 usage-telemetry + runtime-truth read-models and is **Inspector-renderable** (capability note §6). It feeds DOC13 cost views (§8) and is the durable signal a future router learns engine performance from (§3.5). The transcript ELNOR renders for `ec_external_*`/`openclaw_*` runs is labelled `wrapper_composed` vs `native_*` per R14 §0.4 — never presented as native Gateway truth when it is reconstructed from a subprocess/CLI stream.
Lints: `engine.run_without_provenance`, `engine.wrapper_transcript_labeled_native`.
---
## 6. EC integration, egress, secrets, and memory access (bind by reference)
### 6.1 EC policy-evaluator socket
The SDK/engine **permission hooks are the EC policy-evaluator socket** (capability note §6): every agent action the engine wants to take is offered to EC for an allow/deny/confirm decision against the DOC81 R2 gates. Routing is policy-respecting (`ec_policy_consulted`), architect-overridable per run. Obligation: EC §8.
### 6.2 Delegation egress (E0 §22 + DOC81 R2) — referenced, never redefined
Delegating to an external agent **is egress**. An invocation that carries memory content is an egress event classified under **E0 §22 `E0OutboundDestinationClass`** (`cloud_api`, `agent_messaging`) and governed by **E0 §3.3 `DelegationMFC`** + the **DOC81 R2 send-time gate** (typed destination → destination-specific decision → send-time gate; `local_file_export` rules apply to artifacts the engines write). This adapter **binds to those contracts by reference and does not redefine them.**
```ts
interface DelegationEgressBinding {
engine_run_ref: EngineRunRef;
schema_owner: "DOC11";
carries_memory_content: boolean;
e0_outbound_destination_class_ref: string; // E0 §22 — cloud_api | agent_messaging (referenced)
delegation_mfc_ref?: string; // E0 §3.3 (referenced)
doc81_send_time_gate_decision_ref?: string; // DOC81 R2 (referenced)
}
```
> Note: a cloud model call is `cloud_api` egress whether the topology is OpenClaw-native or external — the gate is topology-agnostic. External topologies *add* the `agent_messaging` surface (handing over a commission/files) and, if `mcp_pull` is on, the agent pulling memory back (§6.4).
### 6.3 Secrets hygiene (inline now; **re-homes to DOC5**)
Per capability note §3 rule 8 / §5 rule 4: auth material **never** enters the repo, logs, reports, memory files, prompts, or tool args, and is **never echoed**. Per-engine auth artifacts live in **non-repo folders** (`/Users/OpenClaw1/Elnor/.codex-auth/` pattern; `.claude-auth/` when needed). When the **DOC5** security/credential plane lands, the credential-provider taxonomy, `SecretRef`/`SecretLease` materialization, and the action/egress gate become **DOC5-owned** and this adapter **consumes** them; the bindings in §6.2/§6.4 redirect to DOC5's egress gate (which consumes DOC81). Obligation: DOC5 §8.
Lints: `engine.secret_in_repo_log_report_or_memory`, `engine.secret_in_prompt_or_args`.
### 6.4 Memory access modes (and the Reverse MCP server is referenced, not built)
- **`native_push`** — EC assembles the same context packet via DOC24 / DOC84 and renders it onto the engine's input surface (prompt or working-dir files). Same selection machinery as native delivery; only the render target differs (a DOC84/KDA delivery-target obligation, §8). Subject to the §6.2 send-time gate.
- **`mcp_pull`** — the engine pulls ELNOR memory on demand via the **existing Reverse MCP Server** (DOC16 §16.7.4: *"expose Elnor as an MCP server for external LLMs"*; runtime nominally DOC11, scope enforcement DOC24 §17). This adapter **references** that server; it does **not** define or build it. **Every memory-returning reverse-MCP call is itself an egress** and MUST pass the same §6.2 gate (DOC24 scope enforcement + DOC81/E0). Whether the reverse-MCP runtime stays in DOC11 or moves to DOC5 is OPEN-2.
- **`none`** — only prompt + working-dir; the "no context" setting; used deliberately for independent red-team.
Lint: `engine.mcp_pull_without_send_time_gate`.
### 6.5 File-bus contract (the durable exchange pattern)
For `ec_external_*` topologies, the durable exchange is the proven file bus (capability note §1, §4): commission written into the repo → engine runs against the working dir → report written to a designated folder (e.g. `Reviews/`). The report artifact is captured into `AgentRunRecord.report_artifact_ref` and routed into the normal task-output → extraction pipeline (DOC23/DOC82/DOC25 seams) so memory-saving, extraction, and learning still occur; capture fidelity tracks topology (native > embedded-SDK > CLI) but the pipeline is unchanged. Obligation: §8.
---
## 7. Failure / unhappy paths (per contract)
```ts
type EngineFailureMode =
| "auth_expired_or_revoked" // -> structured blocked state + re-login flow; NEVER silent API fallback (capability note §3 rule 1)
| "version_drift" // pinned != observed -> drift_detected; block or warn per policy
| "rate_limit_or_plan_stall" // -> queued/stalled, surfaced (capability note §5 rule 2)
| "claude_credit_exhausted" // -> block, or explicit API spill if toggle on (§4.4)
| "sandbox_violation" // engine attempted out-of-scope FS/network -> blocked
| "network_loss_mid_run" // -> attempt resume via session_ref, else blocked
| "orphaned_session" // -> reap or resume via session_ref
| "report_not_written" // file-bus report missing -> failed outcome
| "billing_path_mismatch"; // §4.2 -> blocked before run
interface EngineFailure {
engine_run_ref: EngineRunRef;
schema_owner: "DOC11";
failure_mode: EngineFailureMode;
user_visible_message: string; // structured degraded/blocked state, not silent failure (R14 honesty)
recovery_action?: "relogin" | "update_pin" | "wait_or_queue" | "enable_spill" | "resume_session" | "rewrite_report" | "manual";
reason_codes: ReasonCodeId[];
}
```
Hard rule (capability note §3 rule 1; §5 rule 1): on auth failure the adapter surfaces a re-login flow and **never silently falls back to API billing.**
Lints: `engine.silent_api_fallback_on_auth_failure`, `engine.failure_without_structured_state`.
---
## 8. Cross-doc obligations (proposed OP-A rows; DOC10 XDI mirror per R14 §0.7)
| Target | Obligation | Provisional OP-A id |
|---|---|---|
| DOC13 | Engine tier cost rows; Claude Agent SDK credit meter; T2 API-spill cost attribution | `OBL-D13-ENGINE-TIER-COST-01` |
| DOC13 | Plan-window/quota pressure surfaced for subscription engines | `OBL-D13-ENGINE-QUOTA-PRESSURE-01` |
| DOC12 | External agents as room participants where applicable; OpenClaw ACP / Codex Supervisor seam | `OBL-D12-EXTAGENT-ACP-01` |
| DOC20 | Engines settings panel: per-role engine/topology/model/think/permission/tool-allowlist; **auth-source indicator**; **Claude credit meter**; why-routed view; plan-usage meter | `OBL-D20-ENGINES-PANEL-01` |
| EC Core | Engine permission-hook contract (policy-evaluator socket); durable `AgentRunRecord` + receipts writes | `OBL-EC-ENGINE-PERMISSION-HOOK-01` |
| DOC16/DOC24/DOC11-runtime | Reverse MCP server is the `mcp_pull` surface; every memory-returning call gated | `OBL-D16-REVERSE-MCP-PULL-GATE-01` |
| DOC24 / DOC84 (KDA) | "render context packet for external-agent surface" delivery target (for `native_push`) | `OBL-D84-EXTAGENT-PACKET-RENDER-01` |
| DOC23/DOC82/DOC25 | Route engine file-bus reports into the task-output → extraction pipeline | `OBL-D23-EXTAGENT-OUTPUT-INTAKE-01` |
| DOC5 (future) | Re-home credential providers / `SecretLease` / action+egress gate; adapter consumes | `OBL-D5-ENGINE-CRED-REHOME-01` |
| Routing policy spec (future) | Owns rules/signals/weights; consumes DOC11 registry + `ResolvedEnginePlan` | `OBL-ROUTING-ENGINE-SELECTION-01` |
| E0 / DOC81 | Egress binding for delegation (reference only) | `OBL-D81-DELEGATION-EGRESS-REF-01` |
---
## 9. Phase-2 multi-principal note
Per capability note §5 rule 3 + DOC81 R-5: **other users authenticate themselves or ride T2 — never the architect's subscription** (license-bound). Reinforced by Anthropic policy: the Claude Agent SDK credit is **per-user and cannot be pooled**, so any shared/team automation must use an API key (T2), not a subscription seat. Phase-1 single-principal hooks (`auth_source`, per-principal billing path) are present and dormant; the adapter does not implement networking.
---
## 10. Compatibility with R14 and pending R15
**R14 (operative):** conforms to R14 §0 conventions — canonical naming table (§0.6A: new names introduced here are listed for fold-in), Settings-Only honesty (§0.6B), capability-evidence labels (§0.4), desired/effective/executed truth, anti-ghost, DOC10 XDI companion-ledger rows (§0.7). No R14 contract is redefined. New canonical names to add to §0.6A on adoption: `EngineDescriptor`, `EngineInvocation`, `ResolvedEnginePlan`, `RoutingTargetBundle`, `AgentRunRecord`, `BillingPathAssertion`, `ClaudeCreditMeter`, `DelegationEgressBinding`, `EngineFailure`.
**R15 (pending):**
- **§10 (Codex app-server/code-mode migration; RLD-11-13):** the adapter's Codex topologies (`openclaw_runtime_pi` / `openclaw_runtime_codex`) bind to R15's app-server/code-mode model and **do not re-assert a deprecated `codex-cli/*` provider surface**. *This is the one collision surface; it is resolved here by alignment, not contradiction.*
- **§15 (model catalog source-plan/auth/fallback/quota):** the adapter's `auth_source` indicator, `expected_billing_path`, and `ClaudeCreditMeter` are **additive extensions** of §15's auth/source-plan/quota surface — not a second control language.
- **§16 (subagent readback/parent review):** complementary; external-agent runs surface as provenance + (where applicable) ACP participants.
- **§22 (route/event closure) + §23 (acceptance tests):** the redline will need to add this proposal's routes/events to the §22 appendix and its golden scenarios to §23.
---
## 11. OPEN_FOR_ARCHITECT_REVIEW (5 — the cap)
1. **OPEN-1 (verification):** confirm the architect's current OpenClaw build exposes the no-fee Codex OAuth provider (`openclaw_runtime_pi`/`_codex`) and the `claude-cli` backend (enable `codex` plugin + `codex login`; `claude auth login` + `agentRuntime.id: claude-cli`; run `openclaw doctor --fix` to clear legacy pins; `openclaw models status`). On success, flip those rows from `native_documented_not_probed` to `native_probed`.
2. **OPEN-2 (ownership):** does the **Reverse MCP server runtime** stay in DOC11 (DOC16 §16.7.4) or migrate to DOC5 as an inbound-auth/egress surface?
3. **OPEN-3 (scope):** confirm the routing **policy** engine is a separate spec (DOC11 holds only registry + `ResolvedEnginePlan` + override + telemetry).
4. **OPEN-4 (Claude credit defaults):** confirm Max tier for `ClaudeCreditMeter` defaults (5x $100 / 20x $200) and the `on_exhaustion` behavior (default `block` vs `spill_to_api_if_enabled`).
5. **OPEN-5 (naming):** confirm the `engine` × `invocation_topology` canonical vocabulary aligns with R15 §10's OpenClaw runtime-id naming before fold-in to §0.6A.
---
## 12. Lints + golden fixtures (summary)
**Lints** (consolidated): `engine.billing_path_mismatch`, `engine.api_key_in_default_agent_env`, `engine.silent_api_spill`, `engine.claude_run_without_credit_meter`, `engine.run_without_resolved_plan`, `engine.named_agent_target_unknown`, `engine.routing_decision_without_trace`, `engine.run_without_provenance`, `engine.wrapper_transcript_labeled_native`, `engine.secret_in_repo_log_report_or_memory`, `engine.secret_in_prompt_or_args`, `engine.mcp_pull_without_send_time_gate`, `engine.silent_api_fallback_on_auth_failure`, `engine.failure_without_structured_state`.
**Golden fixtures** (executable assertions):
- `fx_codex_oneshot_readonly_audit` — `openai_codex` / `ec_external_cli` / `read_only` / `none`; expects T1 flat, billing-path assert pass, provenance stamped, report intake.
- `fx_claude_p_drafting_with_credit_meter` — `claude` / `openclaw_runtime_claude_cli`; expects `agent_sdk_credit` path, credit meter decremented, warn event near threshold.
- `fx_mcp_pull_governed` — `ec_external_sdk_embedded` / `mcp_pull`; expects each memory-returning reverse-MCP call passes the §6.2 gate.
- `fx_native_push_packet` — `native_push`; expects a DOC24/DOC84-rendered packet, send-time gate decision present.
- `fx_auth_expired_relogin` — expects structured blocked state + re-login, **no** silent API fallback.
- `fx_billing_path_mismatch_blocked` — API key in env where subscription expected → blocked before run.
- `fx_claude_credit_exhausted` — expects block (or explicit toggle spill), surfaced, never silent.
- `fx_multi_principal_phase2` — second principal → per-user auth or T2; never architect subscription.
*End of proposal — `DOC11_PROPOSAL_EXTERNAL_AGENT_ENGINE_ADAPTER.md`. Status: proposal, not operative.*