Files

1213 lines
47 KiB
Python
Raw Permalink Normal View History

2026-08-24 08:11:59 -07:00
#!/usr/bin/env python3
"""Codex binary discovery, generated homes, configuration, and process environment."""
from __future__ import annotations
import contextlib
import copy
import hashlib
import json
import os
import re
import shutil
import subprocess
import tempfile
from collections.abc import Iterable, Mapping, Sequence
from pathlib import Path
from typing import Any
from mmo_app_server import APP_SERVER_LIFECYCLE_TIMEOUT_SECONDS
from mmo_guidance import (
PROFILE_SKILL_NAME,
PROFILE_SKILL_RELATIVE_PATH,
agent_guidance_relative_path,
)
from mmo_profiles import (
agent_mcp_tool_names,
builtin_auth_link_mode,
control_targets,
load_settings,
)
from mmo_profiles import (
mcp_children as _mcp_children,
)
from mmo_profiles import (
native_children as _native_children,
)
from mmo_profiles import (
reachable_native_agent_ids as _reachable_native_agent_ids,
)
from mmo_schema import extract_json_document
from mmo_state import (
ACTIVE_SESSION_STATUSES,
root_mcp_token,
session_dir,
)
from mmo_tool_mcp import (
codex_tool_mcp_server_config,
tool_mcp_environment_names,
tool_mcp_http_environment_names,
)
from mmo_util import (
atomic_write_json,
atomic_write_text,
config_root,
filtered_environment,
install_root,
is_within,
parse_env_file,
read_json,
sha256_file,
state_root,
toml_dumps,
valid_http_header_value,
)
CODEX_MODEL_CATALOG_CACHE_SCHEMA = 2
_BUNDLED_CODEX_CATALOGS: dict[str, dict[str, Any]] = {}
GENERIC_CODEX_MODEL_INSTRUCTIONS = """You are Codex, a coding agent operating in the user's workspace. Follow the system, developer, project, and role instructions provided for this session. Use the available tools to inspect, modify, and validate the workspace. Continue until the assigned task is complete, keep changes within scope, and report results accurately."""
_REASONING_DESCRIPTIONS = {
"minimal": "Minimal reasoning for simple deterministic work",
"low": "Light reasoning for straightforward work",
"medium": "Balanced reasoning depth and latency",
"high": "Greater reasoning depth for complex work",
"xhigh": "Extra-high reasoning depth for difficult work",
"max": "Maximum reasoning depth for the hardest work",
"ultra": "Maximum reasoning with model-native delegation",
}
def _auth_link(home: Path, provider: Mapping[str, Any], settings: Mapping[str, Any]) -> list[str]:
if provider["driver"] != "codex_builtin" or provider.get("auth") != "chatgpt":
return []
mode = builtin_auth_link_mode(provider, settings)
if mode == "none":
return []
base_home = Path(str(settings.get("base_codex_home", "~/.codex"))).expanduser().resolve()
linked: list[str] = []
# Codex 0.149's .credentials.json is MCP OAuth state, not ChatGPT login
# state. Propagating it would expose unrelated remote-MCP credentials.
for name in ("auth.json",):
source = base_home / name
target = home / name
if not source.exists():
continue
if mode == "shared":
if target.is_symlink() and target.resolve() == source.resolve():
pass
else:
if target.exists() or target.is_symlink():
target.unlink()
target.symlink_to(source)
elif mode == "copy":
if target.is_symlink():
target.unlink()
shutil.copy2(source, target)
os.chmod(target, 0o600)
linked.append(name)
return linked
def _codex_catalog_cache_root() -> Path:
root = state_root() / "codex-model-catalogs"
root.mkdir(parents=True, exist_ok=True, mode=0o700)
return root
def _resolved_codex_binary() -> Path | None:
configured = _codex_binary()
candidate = shutil.which(configured)
if candidate:
return Path(candidate).resolve()
path = Path(configured).expanduser()
if path.is_file() and os.access(path, os.X_OK):
return path.resolve()
return None
def require_codex_binary() -> Path:
"""Resolve the configured Codex executable or reject session admission."""
binary = _resolved_codex_binary()
if binary is None:
raise RuntimeError(f"Codex binary not found or not executable: {_codex_binary()}")
return binary
def _codex_binary_fingerprint(binary: Path) -> str:
stat_result = binary.stat()
payload = json.dumps(
{
"path": str(binary),
"size": stat_result.st_size,
"mtime_ns": stat_result.st_mtime_ns,
"schema": CODEX_MODEL_CATALOG_CACHE_SCHEMA,
},
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
return hashlib.sha256(payload).hexdigest()[:24]
def _validated_codex_catalog(document: Any) -> dict[str, Any] | None:
if not isinstance(document, Mapping):
return None
rows = document.get("models")
if not isinstance(rows, list):
return None
models: list[dict[str, Any]] = []
seen: set[str] = set()
for row in rows:
if not isinstance(row, Mapping):
continue
slug = row.get("slug") or row.get("id")
if not isinstance(slug, str) or not slug or slug in seen:
continue
item = copy.deepcopy(dict(row))
item["slug"] = slug
models.append(item)
seen.add(slug)
if not models:
return None
return {"models": models}
def _load_codex_bundled_catalog(binary: Path | None = None) -> dict[str, Any]:
"""Return the exact model metadata bundled with the active Codex binary.
A configured ``model_catalog_json`` is the startup catalog for that Codex
process. Mixed built-in/custom processes therefore need the native rows
copied from the active binary before MMO appends snapshot-specific external
bindings. The cache key is tied to the binary path, size, and mtime.
"""
binary = binary or _resolved_codex_binary()
if binary is None:
raise RuntimeError(f"Codex binary not found: {_codex_binary()}")
fingerprint = _codex_binary_fingerprint(binary)
memory_cached = _BUNDLED_CODEX_CATALOGS.get(fingerprint)
if memory_cached is not None:
return copy.deepcopy(memory_cached)
cache = _codex_catalog_cache_root() / f"{fingerprint}.json"
with contextlib.suppress(OSError, ValueError, json.JSONDecodeError):
cached = _validated_codex_catalog(read_json(cache))
if cached is not None:
_BUNDLED_CODEX_CATALOGS[fingerprint] = copy.deepcopy(cached)
return copy.deepcopy(cached)
with tempfile.TemporaryDirectory(prefix="query-", dir=_codex_catalog_cache_root()) as temporary:
clean_home = Path(temporary) / "codex-home"
clean_home.mkdir(mode=0o700)
result = subprocess.run(
[str(binary), "debug", "models", "--bundled"],
env=filtered_environment(
extra={
"CODEX_HOME": str(clean_home),
"GIT_TERMINAL_PROMPT": "0",
"NO_COLOR": "1",
}
),
text=True,
capture_output=True,
timeout=45,
check=False,
)
document, parse_error = extract_json_document(result.stdout)
catalog = _validated_codex_catalog(document)
if result.returncode != 0 or catalog is None:
detail = result.stderr.strip() or parse_error or "no model rows returned"
raise RuntimeError(
"unable to read Codex's bundled model catalog with "
f"'{binary} debug models --bundled': {detail[-2000:]}"
)
atomic_write_json(cache, catalog, 0o600)
_BUNDLED_CODEX_CATALOGS[fingerprint] = copy.deepcopy(catalog)
return copy.deepcopy(catalog)
def _process_drivers(resolved: Mapping[str, Any], agent_ids: Sequence[str]) -> set[str]:
return {
str(
resolved["routes"][resolved["models"][resolved["agents"][agent_id]["model"]]["route"]][
"driver"
]
)
for agent_id in agent_ids
}
def _process_needs_model_catalog(resolved: Mapping[str, Any], agent_ids: Sequence[str]) -> bool:
return any(driver != "codex_builtin" for driver in _process_drivers(resolved, agent_ids))
def _profile_needs_bundled_codex_catalog(resolved: Mapping[str, Any]) -> bool:
"""Whether any generated Codex process mixes native and external models."""
for agent_id in resolved["agents"]:
relevant = [agent_id, *_reachable_native_agent_ids(resolved, agent_id)]
drivers = _process_drivers(resolved, relevant)
if "codex_builtin" in drivers and any(driver != "codex_builtin" for driver in drivers):
return True
return False
def bundled_codex_catalog_for_profile(
resolved: Mapping[str, Any], binary: Path
) -> dict[str, Any] | None:
"""Load native model rows only for a generated mixed-provider Codex process."""
if not _profile_needs_bundled_codex_catalog(resolved):
return None
return _load_codex_bundled_catalog(binary)
def _reasoning_presets(model: Mapping[str, Any]) -> list[dict[str, str]]:
result: list[dict[str, str]] = []
for effort in model.get("reasoning_levels", []):
value = str(effort)
if value == "none":
continue
result.append(
{
"effort": value,
"description": _REASONING_DESCRIPTIONS.get(value, f"{value} reasoning effort"),
}
)
return result
def _catalog_apply_patch_tool_type(model: Mapping[str, Any]) -> str | None:
"""Return a value accepted by the active Codex ModelInfo schema.
Current Codex releases expose only the ``freeform`` catalog variant. The
generated Codex process always speaks Responses to either the provider or
Switchyard. A compatible endpoint may support ordinary function tools but
reject Responses custom tools, so only models advertising that narrower
capability receive apply_patch. Function tools such as shell and MCP stay
available independently.
"""
return "freeform" if bool(model.get("supports_custom_tools", True)) else None
def _generated_model_catalog_entry(
*,
model: Mapping[str, Any],
binding_model: str,
native_delegation: bool,
visible: bool,
) -> dict[str, Any]:
"""Translate one resolved MMO model into Codex startup metadata.
Codex selects context accounting, reasoning controls, patch-tool shape, and
native-agent support from this row. Snapshot-specific generated bindings must therefore
be exact catalog slugs rather than relying on unknown-model fallback metadata.
The row targets the currently supported Codex ``model_messages`` structure.
MMO generation 8 does not emit retired catalog fields as compatibility shims.
"""
modalities = [
str(item)
for item in model.get("modalities", ["text"])
if str(item) in {"text", "image", "audio"}
]
if "text" not in modalities:
modalities.insert(0, "text")
reasoning = _reasoning_presets(model)
supported = {item["effort"] for item in reasoning}
configured_default = str(model.get("default_reasoning", "none"))
default_reasoning = (
configured_default
if configured_default != "none" and configured_default in supported
else None
)
context_window = int(model["context_window"])
supports_reasoning = bool(model.get("supports_reasoning_summaries", False))
patch_tool = _catalog_apply_patch_tool_type(model)
instructions = GENERIC_CODEX_MODEL_INSTRUCTIONS
entry: dict[str, Any] = {
"slug": binding_model,
"display_name": str(model.get("display_name") or model["upstream_id"]),
"description": str(model.get("description") or "Codex MMO configured model"),
"default_reasoning_level": default_reasoning,
"supported_reasoning_levels": reasoning,
"shell_type": ("shell_command" if model.get("tool_calling", True) else "disabled"),
"visibility": "list" if visible else "hide",
"supported_in_api": True,
"priority": 90,
"additional_speed_tiers": [],
"service_tiers": [],
"default_service_tier": None,
"availability_nux": None,
"upgrade": None,
"model_messages": {
"instructions_template": instructions,
"instructions_variables": None,
"approvals": None,
"collaboration_modes": None,
"auto_review": None,
"permissions": None,
"token_budget": None,
},
"include_skills_usage_instructions": True,
"include_plugin_usage_instructions": False,
"include_apps_usage_instructions": False,
"supports_reasoning_summary_parameter": supports_reasoning,
"default_reasoning_summary": "none",
"support_verbosity": False,
"default_verbosity": None,
"apply_patch_tool_type": patch_tool,
"web_search_tool_type": ("text_and_image" if "image" in modalities else "text"),
"truncation_policy": {"mode": "bytes", "limit": 10_000},
"supports_image_detail_original": "image" in modalities,
"context_window": context_window,
"max_context_window": context_window,
"auto_compact_token_limit": None,
"comp_hash": None,
"effective_context_window_percent": 95,
# Exact Codex 0.149 ModelInfo capability field.
"supports_parallel_tool_calls": bool(model.get("parallel_tool_calls", False)),
"experimental_supported_tools": [],
"input_modalities": modalities,
"supports_search_tool": False,
"use_responses_lite": False,
"auto_review_model_override": None,
"model_specialty": None,
"tool_mode": None,
}
if native_delegation:
# V1 is the broadly compatible custom-agent surface. MMO separately
# accounts for every delegated role in the profile snapshot and lineage.
entry["multi_agent_version"] = "v1"
else:
entry["multi_agent_version"] = None
return entry
def _write_process_model_catalog(
*,
home: Path,
resolved: Mapping[str, Any],
agent_ids: Sequence[str],
bindings: Mapping[str, Mapping[str, Any]],
bundled_catalog: Mapping[str, Any] | None,
) -> Path | None:
if not _process_needs_model_catalog(resolved, agent_ids):
return None
rows: dict[str, dict[str, Any]] = {}
if bundled_catalog is not None:
for row in bundled_catalog.get("models", []):
if isinstance(row, Mapping) and isinstance(row.get("slug"), str):
rows[str(row["slug"])] = copy.deepcopy(dict(row))
binding_drivers: dict[str, set[str]] = {}
process_agent_id = agent_ids[0]
grouped_agents: dict[str, list[str]] = {}
for agent_id in agent_ids:
agent = resolved["agents"][agent_id]
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
binding_model = str(bindings[agent_id]["model"])
binding_drivers.setdefault(binding_model, set()).add(str(provider["driver"]))
grouped_agents.setdefault(binding_model, []).append(agent_id)
conflicts = {
slug: sorted(drivers)
for slug, drivers in binding_drivers.items()
if "codex_builtin" in drivers and len(drivers) > 1
}
if conflicts:
raise ValueError(
"a native Codex process cannot safely use the same model slug through "
"both the built-in and an external provider; use an MCP backend or a "
f"Switchyard binding instead: {conflicts}"
)
for binding_model, members in grouped_agents.items():
agent_id = members[0]
agent = resolved["agents"][agent_id]
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
if provider["driver"] == "codex_builtin" and binding_model in rows:
continue
native_delegation = any(bool(_native_children(resolved, member)) for member in members)
rows[binding_model] = _generated_model_catalog_entry(
model=model,
binding_model=binding_model,
native_delegation=native_delegation,
visible=process_agent_id in members,
)
catalog_path = home / "models.json"
atomic_write_json(catalog_path, {"models": list(rows.values())}, 0o600)
return catalog_path
def _codex_provider_config(
snapshot: Mapping[str, Any],
agent: Mapping[str, Any],
gateway_base_url: str | None,
) -> tuple[str, str, dict[str, Any], list[str]]:
resolved = snapshot["resolved"]
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
driver = provider["driver"]
provider_tables: dict[str, Any] = {}
command_flags: list[str] = []
# Provider idleness is transport failure detection, not a task deadline.
# Keep it well beyond the profile's warning-only stall interval so a slow
# first token cannot erase an otherwise healthy durable goal.
stall_window_ms = int(agent.get("stall_warning_seconds") or 1800) * 2000
provider_idle_ms = int(provider.get("stream_idle_timeout_ms") or 0)
stream_idle_timeout_ms = max(3_600_000, provider_idle_ms, stall_window_ms)
if driver == "switchyard":
if not gateway_base_url:
raise RuntimeError("profile requires Switchyard but no gateway URL is available")
provider_id = "mmo_switchyard"
model_id = snapshot["manifest"]["route_ids"][agent["model"]]
provider_tables[provider_id] = {
"name": f"Codex MMO snapshot {snapshot['manifest']['snapshot_hash'][:12]}",
"base_url": gateway_base_url,
"wire_api": "responses",
"requires_openai_auth": False,
"request_max_retries": 1,
"stream_max_retries": 1,
"stream_idle_timeout_ms": stream_idle_timeout_ms,
}
elif driver == "codex_custom":
provider_id = "mmo_" + re.sub(r"[^a-z0-9_]", "_", model["route"])
model_id = model["upstream_id"]
table: dict[str, Any] = {
"name": provider["name"],
"base_url": provider["base_url"],
"wire_api": provider.get("wire_api", "responses"),
"requires_openai_auth": False,
"request_max_retries": provider["request_max_retries"],
"stream_max_retries": provider["stream_max_retries"],
"stream_idle_timeout_ms": stream_idle_timeout_ms,
}
credentials = list(provider.get("credential_envs", []))
if credentials:
table["env_key"] = credentials[0]
if provider.get("http_headers"):
table["http_headers"] = provider["http_headers"]
if provider.get("env_http_headers"):
table["env_http_headers"] = provider["env_http_headers"]
provider_tables[provider_id] = table
elif driver == "codex_builtin":
provider_id = provider["provider_id"]
model_id = model["upstream_id"]
elif driver == "codex_oss":
provider_id = provider["provider_id"]
model_id = model["upstream_id"]
command_flags = ["--oss", "--local-provider", provider_id]
else:
raise RuntimeError(f"unsupported provider driver: {driver}")
return model_id, provider_id, provider_tables, command_flags
def _tool_mcp_server_ids(resolved: Mapping[str, Any], agent_ids: Sequence[str]) -> list[str]:
return sorted(
{
server_id
for agent_id in agent_ids
for server_id in resolved["agents"][agent_id].get("tool_mcp_servers", {})
}
)
def _tool_mcp_server_configs(
resolved: Mapping[str, Any],
agent_id: str,
server_ids: Sequence[str],
) -> dict[str, dict[str, Any]]:
definitions = resolved.get("tool_mcp_servers", {})
grants = resolved["agents"][agent_id].get("tool_mcp_servers", {})
return {
server_id: codex_tool_mcp_server_config(
definitions[server_id],
grants.get(server_id),
)
for server_id in server_ids
}
def app_server_lifecycle_timeout(resolved: Mapping[str, Any], agent_id: str) -> float:
"""Cover every enabled Tool MCP startup plus bounded protocol overhead."""
grants = resolved["agents"][agent_id].get("tool_mcp_servers", {})
definitions = resolved.get("tool_mcp_servers", {})
configured = [
float(definitions[server_id].get("startup_timeout_sec") or 0) for server_id in grants
]
return max(APP_SERVER_LIFECYCLE_TIMEOUT_SECONDS, max(configured, default=0.0) + 30.0)
def _mesh_tool_timeout(resolved: Mapping[str, Any], agent_id: str) -> float:
"""Keep the outer MCP call alive through every reachable control target."""
agent = resolved["agents"][agent_id]
target_ids = {
agent_id,
*_mcp_children(resolved, agent_id),
*control_targets(agent),
}
return max(app_server_lifecycle_timeout(resolved, target) for target in target_ids) + 30.0
def _binding_bundle(
snapshot: Mapping[str, Any],
agent_ids: Sequence[str],
gateway_base_url: str | None,
*,
process_agent_id: str,
) -> tuple[dict[str, dict[str, Any]], dict[str, Any], list[str]]:
bindings: dict[str, dict[str, Any]] = {}
providers: dict[str, Any] = {}
process_flags: list[str] = []
for agent_id in agent_ids:
agent = snapshot["resolved"]["agents"][agent_id]
model_id, provider_id, tables, flags = _codex_provider_config(
snapshot, agent, gateway_base_url
)
if flags and agent_id != process_agent_id:
raise ValueError(
f"native agent {agent_id} uses Codex OSS mode, which cannot be selected "
"inside a mixed-provider parent process; bind it through Switchyard instead"
)
if agent_id == process_agent_id:
process_flags = flags
for key, value in tables.items():
existing = providers.get(key)
if existing is not None and existing != value:
raise RuntimeError(f"conflicting generated Codex provider table: {key}")
providers[key] = value
bindings[agent_id] = {
"model": model_id,
"model_provider": provider_id,
}
return bindings, providers, process_flags
def _agent_mcp_server_config(
*,
session_id: str,
caller_agent: str,
tool_names: Iterable[str],
native_token: str | None = None,
job_scoped: bool = False,
enabled: bool = True,
tool_timeout_sec: float,
) -> dict[str, Any]:
tools = sorted(set(tool_names))
enabled = bool(enabled and tools)
env = {
"MMO_INSTALL_ROOT": str(install_root()),
"MMO_ROOT_SESSION_ID": session_id,
"MMO_CALLER_AGENT": caller_agent,
}
if native_token is not None:
env["MMO_CALLER_NATIVE"] = "1"
env["MMO_NATIVE_CALLER_TOKEN"] = native_token
forwarded_env = ["MMO_CALLER_TOKEN", "MMO_RUN_ID"]
if job_scoped:
forwarded_env.append("MMO_CALLER_JOB_ID")
server: dict[str, Any] = {
"command": str(install_root() / "libexec" / "mmo_mcp.py"),
"args": [],
"required": bool(enabled),
"enabled": bool(enabled),
"startup_timeout_sec": 10,
# This covers the largest compiled lifecycle timeout reachable through
# this caller's grants, plus protocol overhead. The MCP client must not
# abandon a valid app-server control round trip first.
"tool_timeout_sec": tool_timeout_sec,
"default_tools_approval_mode": "approve",
# Forward the process-scoped caller capability without serializing its
# plaintext into generated config.toml.
"env_vars": forwarded_env,
"env": env,
}
if enabled:
server["enabled_tools"] = tools
server["tools"] = {name: {"approval_mode": "approve"} for name in tools}
return server
def _base_codex_config(
*,
model: Mapping[str, Any],
provider: Mapping[str, Any],
agent: Mapping[str, Any],
binding: Mapping[str, Any],
providers: Mapping[str, Any],
) -> dict[str, Any]:
config: dict[str, Any] = {
"model": binding["model"],
"model_provider": binding["model_provider"],
"project_doc_max_bytes": 65536,
"approval_policy": agent.get("approval_policy", "never"),
"sandbox_mode": agent["permissions"],
"web_search": agent.get("web_search", "disabled"),
"check_for_update_on_startup": False,
"model_providers": dict(providers),
"features": {
"shell_snapshot": True,
"unified_exec": True,
},
"memories": {
"generate_memories": False,
"use_memories": False,
"disable_on_external_context": True,
},
# App-server threads are the durable execution identity. Persist every
# turn so a detached/restarted host can resume or fork exact context.
"history": {"persistence": "save-all"},
"shell_environment_policy": {
"inherit": "core",
"ignore_default_excludes": False,
},
"sandbox_workspace_write": {
"network_access": bool(agent.get("network_access", False)),
"exclude_slash_tmp": False,
"exclude_tmpdir_env_var": False,
},
}
# Built-in model metadata belongs to the active Codex catalog. Pinning a
# static project baseline here can silently override newer account/client
# limits and capabilities. External generated bindings still require explicit values.
if provider["driver"] != "codex_builtin":
config["model_context_window"] = int(model["context_window"])
if agent.get("reasoning") != "none":
config["model_reasoning_effort"] = agent["reasoning"]
if agent.get("plan_reasoning") not in (None, "none"):
config["plan_mode_reasoning_effort"] = agent["plan_reasoning"]
return config
def _snapshot_guidance_text(snapshot: Mapping[str, Any], relative: str) -> str:
directory = Path(str(snapshot["directory"]))
target = directory / relative
if target.is_symlink() or not target.is_file() or not is_within(target.resolve(), directory):
raise RuntimeError(f"compiled guidance is missing or unsafe: {relative}")
text = target.read_text(encoding="utf-8")
expected_hash = snapshot["manifest"].get("payload_files", {}).get(relative)
actual_hash = hashlib.sha256(text.encode("utf-8")).hexdigest()
if not isinstance(expected_hash, str) or actual_hash != expected_hash:
raise RuntimeError(f"compiled guidance failed integrity validation: {relative}")
return text
def _skill_config(skill_path: Path, *, enabled: bool) -> dict[str, Any]:
return {"config": [{"path": str(skill_path), "enabled": enabled}]}
def _native_agent_file(
snapshot: Mapping[str, Any],
agent_id: str,
*,
binding: Mapping[str, Any],
session_id: str,
native_token: str | None,
tool_mcp_server_ids: Sequence[str],
job_scoped: bool,
skill_path: Path | None,
) -> dict[str, Any]:
resolved = snapshot["resolved"]
agent = resolved["agents"][agent_id]
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
config: dict[str, Any] = {
"name": agent["native_name"],
"description": agent.get("description") or f"Codex MMO role {agent_id}",
"developer_instructions": _snapshot_guidance_text(
snapshot, agent_guidance_relative_path(agent_id)
),
"model": binding["model"],
"model_provider": binding["model_provider"],
"approval_policy": agent.get("approval_policy", "never"),
"sandbox_mode": agent["permissions"],
"web_search": agent.get("web_search", "disabled"),
"sandbox_workspace_write": {
"network_access": bool(agent.get("network_access", False)),
"exclude_slash_tmp": False,
"exclude_tmpdir_env_var": False,
},
"agents": {
"enabled": bool(
resolved["coordination"].get("native_nested_delegation", False)
and _native_children(resolved, agent_id)
),
"max_concurrent_threads_per_session": int(
resolved["coordination"]["native_max_concurrent_threads"]
),
"interrupt_message": bool(
resolved["coordination"].get("native_interrupt_message", True)
),
},
}
if provider["driver"] != "codex_builtin":
config["model_context_window"] = int(model["context_window"])
# Summary capability for external route rows is carried by the generated
# ModelInfo row. Codex 0.149 has no ConfigToml field for that capability.
if agent.get("reasoning") != "none":
config["model_reasoning_effort"] = agent["reasoning"]
if agent.get("plan_reasoning") not in (None, "none"):
config["plan_mode_reasoning_effort"] = agent["plan_reasoning"]
coordinates_mcp = bool(agent["can_spawn"] or agent.get("controls"))
if skill_path is not None:
config["skills"] = _skill_config(skill_path, enabled=coordinates_mcp)
elif coordinates_mcp:
raise RuntimeError(f"coordination-capable native agent {agent_id} has no compiled skill")
mcp_servers = _tool_mcp_server_configs(
resolved,
agent_id,
tool_mcp_server_ids,
)
mcp_enabled = bool(_mcp_children(resolved, agent_id) or agent.get("controls"))
# Override an inherited root MCP identity. A generated per-role token lets
# the supervisor recognize a native caller without trusting model-provided
# arguments.
if snapshot["resolved"]["coordination"]["orchestration"] == "hybrid":
mesh_tools = agent_mcp_tool_names(
resolved["agents"],
root_agent=str(resolved["profile"]["root"]),
agent_id=agent_id,
)
mcp_servers["mmo_mesh"] = _agent_mcp_server_config(
session_id=session_id,
caller_agent=agent_id,
tool_names=mesh_tools,
native_token=native_token if mcp_enabled else None,
job_scoped=bool(job_scoped and mcp_enabled),
enabled=mcp_enabled,
tool_timeout_sec=_mesh_tool_timeout(resolved, agent_id),
)
if mcp_servers:
config["mcp_servers"] = mcp_servers
return config
def _render_codex_config(
snapshot: Mapping[str, Any],
agent_id: str,
*,
home: Path,
gateway_base_url: str | None,
session_id: str,
native_tokens: Mapping[str, str],
bundled_catalog: Mapping[str, Any] | None,
availability: Mapping[str, Mapping[str, Any]],
skill_path: Path | None,
disable_native_delegation: bool = False,
) -> tuple[str, list[str], dict[str, str]]:
resolved = snapshot["resolved"]
agent = resolved["agents"][agent_id]
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
native_ids = [
child_id
for child_id in _reachable_native_agent_ids(resolved, agent_id)
if availability.get(resolved["agents"][child_id]["route"], {}).get("available")
]
bindings, providers, command_flags = _binding_bundle(
snapshot,
[agent_id, *native_ids],
gateway_base_url,
process_agent_id=agent_id,
)
config = _base_codex_config(
model=model,
provider=provider,
agent=agent,
binding=bindings[agent_id],
providers=providers,
)
if agent["can_spawn"] or agent.get("controls"):
if skill_path is None:
raise RuntimeError(f"coordination-capable agent {agent_id} has no compiled skill")
config["skills"] = _skill_config(skill_path, enabled=True)
relevant_ids = [agent_id, *native_ids]
tool_mcp_server_ids = _tool_mcp_server_ids(resolved, relevant_ids)
model_catalog = _write_process_model_catalog(
home=home,
resolved=resolved,
agent_ids=relevant_ids,
bindings=bindings,
bundled_catalog=bundled_catalog,
)
if model_catalog is not None:
config["model_catalog_json"] = str(model_catalog)
if any(
resolved["routes"][resolved["models"][resolved["agents"][item]["model"]]["route"]]["driver"]
== "codex_builtin"
for item in relevant_ids
):
# Generated homes receive file-backed auth by link or explicit copy.
# Codex keyring entries are keyed to canonical CODEX_HOME, so `auto`
# would look in a different namespace and could migrate a shared file
# into session-local keyring state during token refresh.
config["cli_auth_credentials_store"] = "file"
native_children = [] if disable_native_delegation else _native_children(resolved, agent_id)
native_files: dict[str, str] = {}
if native_ids:
agents_table: dict[str, Any] = {
"enabled": bool(native_children),
"max_concurrent_threads_per_session": int(
resolved["coordination"]["native_max_concurrent_threads"]
),
"interrupt_message": bool(
resolved["coordination"].get("native_interrupt_message", True)
),
}
agents_directory = home / "agents"
agents_directory.mkdir(parents=True, exist_ok=True, mode=0o700)
for child_id in native_ids:
child = resolved["agents"][child_id]
path = agents_directory / f"{child['native_name']}.toml"
native_files[child_id] = str(path)
agents_table[child["native_name"]] = {
"description": child.get("description") or f"Codex MMO role {child_id}",
"config_file": str(path),
}
child_config = _native_agent_file(
snapshot,
child_id,
binding=bindings[child_id],
session_id=session_id,
native_token=native_tokens.get(child_id),
tool_mcp_server_ids=tool_mcp_server_ids,
job_scoped=bool(
agent_id != resolved["profile"]["root"] and "mcp" in agent.get("backends", [])
),
skill_path=skill_path,
)
atomic_write_text(path, toml_dumps(child_config), 0o600)
config["agents"] = agents_table
config["features"]["multi_agent"] = bool(native_children)
else:
config["agents"] = {"enabled": False}
config["features"]["multi_agent"] = False
mcp_servers = _tool_mcp_server_configs(
resolved,
agent_id,
tool_mcp_server_ids,
)
management_enabled = bool(_mcp_children(resolved, agent_id) or agent.get("controls"))
control_enabled = bool(agent.get("controls"))
is_mcp_worker = bool(
agent_id != resolved["profile"]["root"] and "mcp" in agent.get("backends", [])
)
if management_enabled or control_enabled:
mesh_tools = agent_mcp_tool_names(
resolved["agents"],
root_agent=str(resolved["profile"]["root"]),
agent_id=agent_id,
)
mcp_servers["mmo_mesh"] = _agent_mcp_server_config(
session_id=session_id,
caller_agent=agent_id,
tool_names=mesh_tools,
job_scoped=is_mcp_worker,
enabled=True,
tool_timeout_sec=_mesh_tool_timeout(resolved, agent_id),
)
if mcp_servers:
config["mcp_servers"] = mcp_servers
return toml_dumps(config), command_flags, native_files
def materialize_agent_home(
session_directory: Path,
snapshot: Mapping[str, Any],
agent_id: str,
gateway_base_url: str | None,
*,
session_id: str,
native_tokens: Mapping[str, str],
bundled_catalog: Mapping[str, Any] | None,
availability: Mapping[str, Mapping[str, Any]],
disable_native_delegation: bool = False,
) -> dict[str, Any]:
home = session_directory / "codex-home" / agent_id
home.mkdir(parents=True, exist_ok=True, mode=0o700)
resolved = snapshot["resolved"]
agent = resolved["agents"][agent_id]
skill_path: Path | None = None
if agent["can_spawn"] or agent.get("controls"):
skill_directory = home / "skills" / PROFILE_SKILL_NAME
skill_directory.mkdir(parents=True, exist_ok=True, mode=0o700)
skill_path = skill_directory / "SKILL.md"
atomic_write_text(
skill_path,
_snapshot_guidance_text(snapshot, PROFILE_SKILL_RELATIVE_PATH),
0o600,
)
config_text, command_flags, native_files = _render_codex_config(
snapshot,
agent_id,
home=home,
gateway_base_url=gateway_base_url,
session_id=session_id,
native_tokens=native_tokens,
bundled_catalog=bundled_catalog,
availability=availability,
skill_path=skill_path,
disable_native_delegation=disable_native_delegation,
)
atomic_write_text(home / "config.toml", config_text, 0o600)
atomic_write_text(
home / "AGENTS.md",
_snapshot_guidance_text(snapshot, agent_guidance_relative_path(agent_id)),
0o600,
)
relevant_agent_ids = [agent_id, *_reachable_native_agent_ids(resolved, agent_id)]
linked_auth: list[str] = []
for relevant_id in relevant_agent_ids:
relevant = resolved["agents"][relevant_id]
relevant_model = resolved["models"][relevant["model"]]
relevant_provider = resolved["routes"][relevant_model["route"]]
if relevant_provider["driver"] == "codex_builtin":
linked_auth = _auth_link(home, relevant_provider, load_settings())
break
direct_credential_groups: list[list[str]] = []
direct_header_envs: list[str] = []
seen_groups: set[tuple[str, ...]] = set()
seen_header_envs: set[str] = set()
for relevant_id in relevant_agent_ids:
relevant = resolved["agents"][relevant_id]
relevant_model = resolved["models"][relevant["model"]]
relevant_provider = resolved["routes"][relevant_model["route"]]
if relevant_provider["driver"] != "codex_custom":
continue
group = tuple(relevant_provider.get("credential_envs", []))
if group and group not in seen_groups:
seen_groups.add(group)
direct_credential_groups.append(list(group))
for env_name in relevant_provider.get("env_http_headers", {}).values():
if env_name not in seen_header_envs:
seen_header_envs.add(env_name)
direct_header_envs.append(env_name)
tool_mcp_envs = sorted(
{
env_name
for server_id in _tool_mcp_server_ids(resolved, relevant_agent_ids)
for env_name in tool_mcp_environment_names(
resolved.get("tool_mcp_servers", {})[server_id]
)
}
)
tool_mcp_http_envs = sorted(
{
env_name
for server_id in _tool_mcp_server_ids(resolved, relevant_agent_ids)
for env_name in tool_mcp_http_environment_names(
resolved.get("tool_mcp_servers", {})[server_id]
)
}
)
model = resolved["models"][agent["model"]]
provider = resolved["routes"][model["route"]]
model_catalog_path = home / "models.json"
return {
"home": str(home),
"command_flags": command_flags,
"linked_auth": linked_auth,
"model": model["upstream_id"],
"route": model["route"],
"driver": provider["driver"],
"credential_envs": list(provider.get("credential_envs", [])),
"direct_credential_groups": direct_credential_groups,
"direct_header_envs": direct_header_envs,
"tool_mcp_envs": tool_mcp_envs,
"tool_mcp_http_envs": tool_mcp_http_envs,
"native_agent_files": native_files,
"orchestration_skill": str(skill_path) if skill_path is not None else None,
"orchestration_skill_sha256": sha256_file(skill_path) if skill_path is not None else None,
"model_catalog_json": str(model_catalog_path) if model_catalog_path.is_file() else None,
"model_catalog_sha256": sha256_file(model_catalog_path)
if model_catalog_path.is_file()
else None,
}
def _persistent_agent_home(
directory: Path,
session: Mapping[str, Any],
agent_id: str,
) -> Path:
codex_home_root = directory / "codex-home"
expected_raw = codex_home_root / agent_id
if codex_home_root.is_symlink() or expected_raw.is_symlink():
raise RuntimeError(f"persistent Codex home cannot be a symlink for {agent_id}")
expected = expected_raw.resolve()
recorded = Path(str(session["homes"][agent_id]["home"])).expanduser().resolve()
if (
recorded != expected
or not expected.is_dir()
or not is_within(expected, directory.resolve())
):
raise RuntimeError(f"persistent Codex home is missing or invalid for {agent_id}")
return expected
def refresh_session_homes(
session: Mapping[str, Any],
snapshot: Mapping[str, Any],
*,
gateway_base_url: str | None,
availability: Mapping[str, Mapping[str, Any]],
native_tokens: Mapping[str, str],
) -> dict[str, Any]:
directory = session_dir(str(session["session_id"]))
homes: dict[str, Any] = {}
resolved = snapshot["resolved"]
for agent_id in resolved["agents"]:
expected_home = _persistent_agent_home(directory, session, agent_id)
relevant = [agent_id, *_reachable_native_agent_ids(resolved, agent_id)]
drivers = _process_drivers(resolved, relevant)
bundled_catalog: Mapping[str, Any] | None = None
catalog_path = expected_home / "models.json"
if _process_needs_model_catalog(resolved, relevant):
expected_catalog_hash = session["homes"][agent_id].get("model_catalog_sha256")
if (
not isinstance(expected_catalog_hash, str)
or not re.fullmatch(r"[0-9a-f]{64}", expected_catalog_hash)
or not catalog_path.is_file()
or catalog_path.is_symlink()
or sha256_file(catalog_path) != expected_catalog_hash
):
raise RuntimeError(
f"pinned generated model catalog failed integrity validation for {agent_id}"
)
if "codex_builtin" in drivers and any(item != "codex_builtin" for item in drivers):
with contextlib.suppress(OSError, ValueError, json.JSONDecodeError):
bundled_catalog = _validated_codex_catalog(read_json(catalog_path))
if bundled_catalog is None:
raise RuntimeError(
f"pinned generated model catalog is missing or invalid for {agent_id}"
)
homes[agent_id] = materialize_agent_home(
directory,
snapshot,
agent_id,
gateway_base_url,
session_id=str(session["session_id"]),
native_tokens=native_tokens,
bundled_catalog=bundled_catalog,
availability=availability,
disable_native_delegation=bool(session.get("tainted")),
)
return homes
def _credential_environment_for_agent(
session: Mapping[str, Any], agent_id: str
) -> tuple[dict[str, str], list[str]]:
groups = session["homes"][agent_id].get("direct_credential_groups", [])
header_envs = session["homes"][agent_id].get("direct_header_envs", [])
tool_mcp_envs = session["homes"][agent_id].get("tool_mcp_envs", [])
tool_mcp_http_envs = set(session["homes"][agent_id].get("tool_mcp_http_envs", []))
if not groups and not header_envs and not tool_mcp_envs:
return {}, []
values = parse_env_file(config_root() / "credentials.env")
values.update({key: value for key, value in os.environ.items() if value})
supplied: dict[str, str] = {}
allowed: list[str] = []
for raw_group in groups:
credentials = [str(name) for name in raw_group]
source = next((name for name in credentials if values.get(name)), None)
if source is None:
raise RuntimeError("missing direct-provider credential: " + "/".join(credentials))
target = credentials[0]
supplied[target] = values[source]
allowed.append(target)
for raw_name in header_envs:
env_name = str(raw_name)
value = values.get(env_name)
# Codex treats a missing or blank env-backed header as optional and
# omits it. Preserve that contract while ensuring a configured secret
# is not removed by filtered_environment().
if value is None or not value.strip():
continue
if not valid_http_header_value(value):
raise RuntimeError(
f"direct-provider header environment variable {env_name} "
"contains a prohibited control character"
)
supplied[env_name] = value
allowed.append(env_name)
for raw_name in tool_mcp_envs:
env_name = str(raw_name)
value = values.get(env_name)
if value is None:
continue
if env_name in tool_mcp_http_envs:
if not value.strip():
continue
if not valid_http_header_value(value):
raise RuntimeError(
f"tool MCP HTTP environment variable {env_name} "
"contains a prohibited control character"
)
elif "\x00" in value:
raise RuntimeError(f"tool MCP environment variable {env_name} contains NUL")
supplied[env_name] = value
allowed.append(env_name)
return supplied, allowed
def session_environment(
session: Mapping[str, Any],
agent_id: str,
*,
caller_job_id: str | None = None,
caller_token: str | None = None,
interactive: bool = False,
) -> dict[str, str]:
run_id = session.get("current_run_id")
if not isinstance(run_id, str) or session.get("status") not in ACTIVE_SESSION_STATUSES:
raise RuntimeError("session has no active execution run")
credential_values, allow_sensitive = _credential_environment_for_agent(session, agent_id)
extra = {
**credential_values,
"CODEX_HOME": session["homes"][agent_id]["home"],
"MMO_INSTALL_ROOT": str(install_root()),
"MMO_CONFIG_ROOT": str(config_root()),
"MMO_STATE_ROOT": str(state_root()),
"MMO_ROOT_SESSION_ID": session["session_id"],
"MMO_RUN_ID": run_id,
"MMO_PROFILE_SNAPSHOT": session["snapshot_hash"],
"MMO_CALLER_AGENT": agent_id,
"MMO_ALLOWED_ROOT": session["allowed_root"],
"MMO_SESSION_DIR": str(session_dir(session["session_id"])),
"GIT_TERMINAL_PROMPT": "0",
}
# Keep worker/event logs deterministic, but never disable the foreground
# Codex TUI's color capabilities. An explicit user NO_COLOR setting is
# still inherited and respected by filtered_environment().
if not interactive:
extra["NO_COLOR"] = "1"
if caller_job_id:
extra["MMO_CALLER_JOB_ID"] = caller_job_id
if not caller_token:
raise RuntimeError("worker MCP caller capability token is missing")
elif caller_token is None:
caller_token = root_mcp_token(str(session["session_id"]))
if caller_token:
extra["MMO_CALLER_TOKEN"] = caller_token
return filtered_environment(allow_sensitive=allow_sensitive, extra=extra)
def _codex_binary() -> str:
settings = load_settings()
configured = str(settings.get("codex_bin", "codex"))
return os.environ.get("MMO_CODEX_BIN") or configured