Files
codex-mmo/libexec/worker_runner.py
T
2026-08-24 08:11:59 -07:00

2422 lines
97 KiB
Python
Executable File

#!/usr/bin/env python3
"""Detached runner for one generic, profile-pinned Codex MMO participant."""
from __future__ import annotations
import atexit
import contextlib
import hashlib
import json
import mimetypes
import os
import signal
import socket
import sys
import threading
import time
from collections.abc import Callable, Mapping
from pathlib import Path
from typing import Any
from mmo_app_server import (
APP_SERVER_INITIALIZE_TIMEOUT_SECONDS,
APP_SERVER_LIFECYCLE_TIMEOUT_SECONDS,
APP_SERVER_RECOVERY_DELAYS_SECONDS,
AppServerClient,
AppServerError,
PersistentThreadHost,
app_server_listen_command,
app_server_socket_path,
bounded_goal_objective,
normalize_turn_failure,
receive_control_request,
require_app_server_codex_version,
retain_partial_evidence,
send_control_response,
validate_server_request_response,
)
from mmo_app_server import (
last_agent_message as _last_agent_message,
)
from mmo_app_server import (
turn_input as _turn_input,
)
from mmo_codex_home import session_environment
from mmo_gateway import route_telemetry, route_telemetry_warnings
from mmo_runtime import taint_session
from mmo_schema import extract_json_document, validate_instance
from mmo_state import (
append_audit,
publish_job_record,
read_job_record,
read_session_record,
runtime_lock_path,
session_dir,
terminate_recorded_process_group,
)
from mmo_util import (
atomic_write_json,
event_usage,
file_lock,
package_version,
process_matches,
process_start_token,
shell_exit_status,
strict_json_loads,
utc_now,
)
from mmo_version import SWITCHYARD_MCP_NAMESPACE_BRIDGE_VERSION
from mmo_workspace import capture_isolated_patch, path_within_scope, remove_isolated_worktree
_TERMINAL_OR_CANCELLING_STATUSES = {
"cancelling",
"completed",
"completed_with_warnings",
"failed",
"stopped",
"cancelled",
}
_CODEX_UNSUPPORTED_OUTPUT_SCHEMA_KEYWORDS = frozenset(
{
"$id",
"$schema",
"allOf",
"else",
"if",
"not",
"oneOf",
"then",
"uniqueItems",
}
)
_CODEX_OUTPUT_SCHEMA_FORMATS = frozenset(
{
"date",
"date-time",
"duration",
"email",
"hostname",
"ipv4",
"ipv6",
"time",
"uuid",
}
)
class WorkerRunner:
"""Own the process-scoped stop signal and one durable worker app-server client."""
def __init__(self, directory: Path) -> None:
self.directory = directory
self.stop_requested = False
self.app_server: AppServerClient | None = None
def install_signal_handlers(self) -> None:
signal.signal(signal.SIGTERM, self.handle_signal)
signal.signal(signal.SIGINT, self.handle_signal)
def handle_signal(self, _signum: int, _frame: object) -> None:
self.stop_requested = True
# Do not acquire AppServerClient locks or wait from a Python signal
# handler. Retire the separately isolated app-server group so any blocked
# request wakes; the normal runner path retains evidence and publishes the
# cancelled terminal state.
client = self.app_server
if client is not None and client.pid is not None:
with contextlib.suppress(ProcessLookupError, PermissionError):
os.killpg(client.pid, signal.SIGTERM)
def terminate_child(self) -> None:
client = self.app_server
if client is not None:
if client.process is not None:
client.stop_host(grace_seconds=8.0)
else:
client.close()
with contextlib.suppress(OSError, RuntimeError, ValueError):
terminate_recorded_process_group(
_read_metadata(self.directory),
prefix="app_server",
grace_seconds=8.0,
)
def run(self) -> int:
return _run_worker(self)
def _read_metadata(directory: Path) -> dict[str, Any]:
return read_job_record(directory)
def _codex_output_schema_errors(schema: Any) -> list[str]:
"""Return reasons an MMO contract cannot safely be sent to Codex as an output schema."""
errors: list[str] = []
if not isinstance(schema, dict) or schema.get("type") != "object":
return ["$: Codex output schemas must have an object root"]
def visit(value: Any, path: str) -> None:
if not isinstance(value, dict):
errors.append(f"{path}: Codex output schemas require object subschemas")
return
for keyword in sorted(_CODEX_UNSUPPORTED_OUTPUT_SCHEMA_KEYWORDS & value.keys()):
errors.append(f"{path}: Codex output schemas do not support {keyword}")
schema_format = value.get("format")
if schema_format is not None and schema_format not in _CODEX_OUTPUT_SCHEMA_FORMATS:
errors.append(f"{path}.format: Codex output schemas do not support {schema_format!r}")
schema_type = value.get("type")
schema_types = set(schema_type) if isinstance(schema_type, list) else {schema_type}
object_schema = "object" in schema_types or "properties" in value
if object_schema:
properties = value.get("properties")
required = value.get("required")
if not isinstance(properties, dict):
errors.append(f"{path}.properties: Codex object schemas require properties")
elif not isinstance(required, list) or set(required) != set(properties):
errors.append(f"{path}: every Codex object-schema property must be required")
if value.get("additionalProperties") is not False:
errors.append(f"{path}: Codex object schemas require additionalProperties=false")
if isinstance(properties, dict):
for key, child in properties.items():
visit(child, f"{path}.properties.{key}")
if "items" in value:
visit(value["items"], f"{path}.items")
variants = value.get("anyOf")
if isinstance(variants, list):
for index, child in enumerate(variants):
visit(child, f"{path}.anyOf[{index}]")
visit(schema, "$")
return errors
def _codex_output_schema(schema: dict[str, Any]) -> dict[str, Any] | None:
"""Project a full MMO contract onto Codex's strict-output schema subset.
The complete contract remains in the prompt and is always applied by MMO's
final validator. This projection removes validation-only keywords that the
app server cannot accept, while preserving the document shape, scalar
types, bounds, and enums that make malformed JSON much less likely.
"""
dropped = {
"$id",
"$schema",
"allOf",
"else",
"if",
"not",
"then",
"uniqueItems",
}
def project(value: Any) -> Any:
if isinstance(value, list):
return [project(item) for item in value]
if not isinstance(value, dict):
return value
result: dict[str, Any] = {}
for key, child in value.items():
if key in dropped:
continue
if key == "format" and child not in _CODEX_OUTPUT_SCHEMA_FORMATS:
continue
target_key = "anyOf" if key == "oneOf" else key
result[target_key] = project(child)
properties = result.get("properties")
schema_type = result.get("type")
schema_types = set(schema_type) if isinstance(schema_type, list) else {schema_type}
if isinstance(properties, dict) or "object" in schema_types:
if not isinstance(properties, dict):
return result
required = value.get("required")
required_names = set(required) if isinstance(required, list) else set()
# Codex requires every advertised property to be required. Omitting
# optional properties from the transport projection preserves the
# original contract's omission semantics instead of coercing a
# model into inventing a value merely to satisfy transport shape.
properties = {key: child for key, child in properties.items() if key in required_names}
result["properties"] = properties
result["additionalProperties"] = False
result["required"] = list(properties)
return result
projected = project(schema)
if not isinstance(projected, dict) or _codex_output_schema_errors(projected):
return None
return projected
def update(directory: Path, **changes: Any) -> dict[str, Any]:
# The supervisor and detached runner may both publish metadata during the
# launch/cancellation boundary. Serialize updates through the same lock as
# admission so neither side can revert a newer state with stale data.
with file_lock(runtime_lock_path()):
data = _read_metadata(directory)
# Reader notifications and control replies are asynchronous. They may
# arrive after an operator has closed admission or after final result
# publication; never let a late "running"/"waiting" update resurrect
# a cancelling or terminal worker.
if (
data.get("status") in _TERMINAL_OR_CANCELLING_STATUSES
and changes.get("status") != "cancelled"
):
changes.pop("status", None)
data.update(changes)
publish_job_record(directory, data)
return data
def _settle_recovery_control(
directory: Path,
state: Mapping[str, Any],
*,
status: str,
error: str | None = None,
) -> bool:
"""CAS one suspended-host relaunch acknowledgement against its exact revision."""
if status not in {"applied", "failed", "delivery_unknown"}:
raise ValueError(f"invalid recovery-control status: {status}")
revision = state.get("recovery_control_revision")
action = state.get("recovery_action")
if (
not isinstance(revision, int)
or isinstance(revision, bool)
or action not in {"continue", "finalize"}
):
return False
with file_lock(runtime_lock_path()):
current = _read_metadata(directory)
if (
current.get("control_revision") != revision
or current.get("last_control_action") != action
or current.get("last_control_status") != "pending"
):
return False
current["last_control_status"] = status
if error is None:
current.pop("last_control_error", None)
else:
current["last_control_error"] = error
publish_job_record(directory, current)
return True
def finalize(
directory: Path,
*,
runner: WorkerRunner | None = None,
**changes: Any,
) -> tuple[dict[str, Any], bool]:
"""Publish one terminal state atomically; cancellation is authoritative."""
with file_lock(runtime_lock_path()):
data = _read_metadata(directory)
cancelled = data.get("status") in {"cancelling", "cancelled"} or bool(
runner and runner.stop_requested
)
if (
data.get("status")
in {
"completed",
"completed_with_warnings",
"failed",
"stopped",
"cancelled",
}
and not cancelled
):
return data, False
requested_status = str(changes.pop("status"))
data.update(changes)
if cancelled:
data["status"] = "cancelled"
data["finished_at"] = data.get("finished_at") or utc_now()
data.pop("error", None)
else:
data["status"] = requested_status
data["finished_at"] = data.get("finished_at") or utc_now()
publish_job_record(directory, data)
return data, cancelled
def begin_running(
directory: Path,
*,
runner: WorkerRunner | None = None,
) -> dict[str, Any] | None:
"""Publish running state without resurrecting a cancelling job."""
with file_lock(runtime_lock_path()):
data = _read_metadata(directory)
if data.get("status") in {"cancelling", "cancelled"} or bool(
runner and runner.stop_requested
):
data.update(
status="cancelled",
finished_at=data.get("finished_at") or utc_now(),
warning="worker launch was cancelled before Codex started",
)
publish_job_record(directory, data)
return None
if data.get("status") not in {"queued", "recovering"}:
data.update(
status="failed",
finished_at=utc_now(),
error=f"worker runner expected queued state, found {data.get('status')!r}",
)
publish_job_record(directory, data)
return None
start_token = process_start_token(os.getpid())
if start_token is None:
data.update(
status="failed",
finished_at=utc_now(),
error="unable to fingerprint worker runner process",
)
publish_job_record(directory, data)
return None
data.update(
worker_runtime_package_version=package_version(),
worker_runtime_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
status="running",
started_at=data.get("started_at") or utc_now(),
runner_pid=os.getpid(),
runner_start_token=start_token,
)
publish_job_record(directory, data)
return data
def _cleanup_worktree_on_exit(directory: Path) -> None:
with contextlib.suppress(Exception):
current = _read_metadata(directory)
if current.get("status") in {"suspended", "recovering", "paused"}:
return
remove_isolated_worktree(current)
def _walk_objects(value: Any) -> list[dict[str, Any]]:
objects: list[dict[str, Any]] = []
if isinstance(value, dict):
objects.append(value)
for child in value.values():
objects.extend(_walk_objects(child))
elif isinstance(value, list):
for child in value:
objects.extend(_walk_objects(child))
return objects
def _verify_literal_result(
structured: Any, literal_task: dict[str, Any], cwd: Path
) -> tuple[list[str], list[dict[str, Any]]]:
errors: list[str] = []
artifacts: list[dict[str, Any]] = []
operation = literal_task["operation"]
objects = _walk_objects(structured)
if operation == "summarize_supplied":
expected = literal_task["input_sha256"]
if not any(item.get("input_sha256") == expected for item in objects):
errors.append("literal summary does not correlate to supplied input_sha256")
return errors, artifacts
evidence = [
item for item in objects if {"path", "sha256", "start_line", "end_line"}.issubset(item)
]
if not evidence:
return ["literal result contains no path/hash/line evidence"], artifacts
allowed_paths = literal_task.get("paths", [literal_task.get("path")])
query = literal_task.get("needle") or literal_task.get("symbol")
for index, item in enumerate(evidence):
raw_path = item.get("path")
if not isinstance(raw_path, str) or Path(raw_path).is_absolute():
errors.append(f"literal evidence {index} has invalid relative path")
continue
path = (cwd / raw_path).resolve(strict=False)
try:
relative = path.relative_to(cwd).as_posix()
except ValueError:
errors.append(f"literal evidence {index} escapes cwd")
continue
if not any(path_within_scope(relative, [str(scope)]) for scope in allowed_paths if scope):
errors.append(f"literal evidence {index} is outside requested paths")
continue
if not path.is_file() or path.is_symlink():
errors.append(f"literal evidence {index} does not identify a regular file")
continue
content = path.read_bytes()
observed_hash = hashlib.sha256(content).hexdigest()
if item.get("sha256") != observed_hash:
errors.append(f"literal evidence {index} file hash does not match")
start = item.get("start_line")
end = item.get("end_line")
lines = content.decode("utf-8", errors="replace").splitlines()
if (
not isinstance(start, int)
or isinstance(start, bool)
or not isinstance(end, int)
or isinstance(end, bool)
or start < 1
or end < start
or end > len(lines)
):
errors.append(f"literal evidence {index} has invalid line range")
continue
excerpt = "\n".join(lines[start - 1 : end])
if "excerpt" in item and item["excerpt"] != excerpt:
errors.append(f"literal evidence {index} excerpt does not match file lines")
if query is not None and query not in excerpt:
errors.append(f"literal evidence {index} does not contain requested literal")
artifacts.append(
{
"relative_path": relative,
"sha256": observed_hash,
"size": len(content),
"media_type": mimetypes.guess_type(path.name)[0] or "application/octet-stream",
"start_line": start,
"end_line": end,
}
)
if operation == "extract" and not any(
item.get("path") == literal_task["path"]
and item.get("sha256") == literal_task["sha256"]
and item.get("start_line") == literal_task["start_line"]
and item.get("end_line") == literal_task["end_line"]
for item in evidence
):
errors.append("literal extraction does not correlate to the requested file/range/hash")
return errors, artifacts
def _correlate_command_evidence(structured: Any, events_path: Path) -> list[str]:
declarations = [
item
for item in _walk_objects(structured)
if isinstance(item.get("command"), str) and "exit_code" in item
]
if not declarations:
return []
observations: list[tuple[str, int]] = []
if events_path.is_file():
for line in events_path.read_text(encoding="utf-8", errors="replace").splitlines():
with contextlib.suppress(json.JSONDecodeError, ValueError):
row = strict_json_loads(line)
for item in _walk_objects(row):
raw_command = item.get("command", item.get("cmd"))
raw_exit = item.get("exit_code", item.get("exitCode"))
if isinstance(raw_command, list) and all(
isinstance(part, str) for part in raw_command
):
raw_command = " ".join(raw_command)
if (
isinstance(raw_command, str)
and isinstance(raw_exit, int)
and not isinstance(raw_exit, bool)
):
observations.append((raw_command, raw_exit))
errors: list[str] = []
for index, declaration in enumerate(declarations):
command = str(declaration["command"])
exit_code = declaration.get("exit_code")
matching_commands = [
observed_exit
for observed_command, observed_exit in observations
if command in observed_command or observed_command in command
]
if not matching_commands:
errors.append(
f"declared command evidence {index} is absent from captured command events"
)
elif exit_code not in matching_commands:
errors.append(
f"declared command evidence {index} exit_code does not match captured events"
)
return errors
def _correlate_artifact_evidence(
structured: Any, cwd: Path
) -> tuple[list[str], list[dict[str, Any]]]:
"""Verify every declared relative-path/hash artifact against the delegated tree."""
declarations = [
item for item in _walk_objects(structured) if "relative_path" in item and "sha256" in item
]
errors: list[str] = []
artifacts: list[dict[str, Any]] = []
cwd = cwd.resolve()
for index, declaration in enumerate(declarations):
raw_path = declaration.get("relative_path")
expected_hash = declaration.get("sha256")
if not isinstance(raw_path, str) or not raw_path or Path(raw_path).is_absolute():
errors.append(f"declared artifact {index} has invalid relative_path")
continue
path = (cwd / raw_path).resolve(strict=False)
try:
relative = path.relative_to(cwd).as_posix()
except ValueError:
errors.append(f"declared artifact {index} escapes delegated cwd")
continue
if not path.is_file() or path.is_symlink():
errors.append(f"declared artifact {index} does not identify a regular file")
continue
content = path.read_bytes()
observed_hash = hashlib.sha256(content).hexdigest()
if expected_hash != observed_hash:
errors.append(f"declared artifact {index} hash does not match")
continue
artifacts.append(
{
"relative_path": relative,
"sha256": observed_hash,
"size": len(content),
"media_type": mimetypes.guess_type(path.name)[0] or "application/octet-stream",
"state": "observed",
}
)
return errors, artifacts
def _validate_result_contract(
result_text: str,
*,
metadata: Mapping[str, Any],
cwd: Path,
events_path: Path,
) -> tuple[Any, bool | None, list[str], list[dict[str, Any]]]:
"""Validate one candidate through the complete mechanically owned contract path."""
contract = metadata.get("output_contract")
if contract is None:
return None, None, [], []
structured, extraction_error = extract_json_document(result_text)
errors = [extraction_error] if extraction_error else validate_instance(structured, contract)
artifacts: list[dict[str, Any]] = []
if not extraction_error:
errors.extend(_correlate_command_evidence(structured, events_path))
artifact_errors, artifacts = _correlate_artifact_evidence(structured, cwd)
errors.extend(artifact_errors)
literal_task = metadata.get("literal_task")
if isinstance(literal_task, dict):
literal_errors, literal_artifacts = _verify_literal_result(
structured, literal_task, cwd
)
errors.extend(literal_errors)
artifacts.extend(literal_artifacts)
return structured, not errors, errors, artifacts
def _successful_current_turn_result(turn: Any, expected_turn_id: Any) -> str:
"""Return only the successful terminal message for the expected current turn."""
if (
not isinstance(turn, Mapping)
or not isinstance(expected_turn_id, str)
or turn.get("id") != expected_turn_id
or turn.get("status") != "completed"
):
return ""
return _last_agent_message(turn)
def _strict_result_candidate_is_valid(
result_text: str,
*,
metadata: Mapping[str, Any],
cwd: Path,
events_path: Path,
) -> bool:
"""Accept budget-boundary recovery only when strict validation is conclusive."""
if metadata.get("contract_enforcement") != "strict" or metadata.get("output_contract") is None:
return False
_structured, valid, _errors, _artifacts = _validate_result_contract(
result_text,
metadata=metadata,
cwd=cwd,
events_path=events_path,
)
return valid is True
def _extract_usage(events_path: Path) -> dict[str, int]:
return event_usage(events_path)
def _partial_result(directory: Path, *, reason: str) -> dict[str, Any]:
"""Retain deterministic readable evidence even without a valid final contract."""
metadata = _read_metadata(directory)
changes = retain_partial_evidence(metadata, directory, reason=reason)
if metadata.get("sandbox_mode") == "workspace-write":
cwd = Path(str(metadata["cwd"])).resolve()
boundary_errors, artifacts, patch = capture_isolated_patch(metadata, directory, cwd)
changes.update(artifacts=artifacts, patch=patch)
if boundary_errors:
changes["partial_boundary_errors"] = boundary_errors
update(directory, **changes)
return changes
def _retire_control_socket(
directory: Path,
socket_path: Path,
bound_identity: tuple[int, int],
) -> None:
"""Unpublish only the control-socket generation owned by this runner."""
with contextlib.suppress(Exception), file_lock(runtime_lock_path()):
owned_socket = False
path_absent = not socket_path.exists() and not socket_path.is_symlink()
if not path_absent and not socket_path.is_symlink():
current_stat = socket_path.stat(follow_symlinks=False)
owned_socket = (current_stat.st_dev, current_stat.st_ino) == bound_identity
if owned_socket:
socket_path.unlink()
if owned_socket or path_absent:
current = _read_metadata(directory)
if current.get("control_socket_path") == str(socket_path):
current["control_socket_ready"] = False
publish_job_record(directory, current)
def _serve_control(
directory: Path,
state: dict[str, Any],
state_lock: threading.RLock,
stop_event: threading.Event,
ready_event: threading.Event,
) -> None:
metadata = _read_metadata(directory)
socket_path = Path(str(metadata.get("control_socket_path", "")))
expected_socket = app_server_socket_path(f"control:job:{metadata['job_id']}")
if socket_path != expected_socket:
raise RuntimeError("worker control socket does not match its canonical job identity")
with contextlib.ExitStack() as stack:
server = stack.enter_context(socket.socket(socket.AF_UNIX, socket.SOCK_STREAM))
# Serialize path replacement and publication with controller relaunches
# and retiring generations. The stable path may name different socket
# inodes over a job's lifetime; only its owning generation may unlink it.
with file_lock(runtime_lock_path()):
if socket_path.exists() or socket_path.is_symlink():
if socket_path.is_symlink() or not socket_path.is_socket():
raise RuntimeError("worker control socket path is unsafe")
socket_path.unlink()
server.bind(str(socket_path))
os.chmod(socket_path, 0o600)
server.listen(8)
server.settimeout(0.5)
bound_stat = socket_path.stat(follow_symlinks=False)
bound_identity = (bound_stat.st_dev, bound_stat.st_ino)
stack.callback(_retire_control_socket, directory, socket_path, bound_identity)
current = _read_metadata(directory)
current.update(control_socket_path=str(socket_path), control_socket_ready=True)
publish_job_record(directory, current)
ready_event.set()
while not stop_event.is_set():
try:
connection, _ = server.accept()
except TimeoutError:
continue
with connection:
connection.settimeout(30.0)
try:
request = receive_control_request(connection)
action = str(request.get("action", ""))
with state_lock:
candidate_client = state.get("client")
if not isinstance(candidate_client, AppServerClient):
raise RuntimeError("worker app-server client is unavailable")
client = candidate_client
thread_value = state.get("thread_id")
if not isinstance(thread_value, str) or not thread_value:
raise RuntimeError("worker app-server thread is unavailable")
thread_id = thread_value
turn_id = state.get("active_turn_id")
inspection = {
"agent_run_ref": state.get("agent_run_ref"),
"thread_id": thread_id,
"active_turn_id": turn_id,
"paused": bool(state.get("paused")),
"current_effort": state.get("current_effort"),
"goal": state.get("goal"),
"execution_mode": state.get("execution_mode"),
}
# Never hold state_lock across an app-server round trip.
# Codex may publish a notification before the response, and
# the reader callback needs this lock to consume it.
if action == "inspect":
result = {
**inspection,
"pending_requests": client.pending_server_requests_for_thread(
thread_id
),
}
elif action == "steer":
if not turn_id:
raise RuntimeError("worker has no active turn to steer")
result = client.request(
"turn/steer",
{
"threadId": thread_id,
"expectedTurnId": str(request["expected_turn_id"]),
"input": _turn_input(str(request["input"]), []),
},
)
elif action in {"interrupt", "pause"}:
if action == "pause" and state["execution_mode"] == "goal":
host = state.get("host")
if not isinstance(host, PersistentThreadHost):
raise RuntimeError("worker host is unavailable")
host.set_goal(status="paused")
if not turn_id:
result = {"interrupted": False}
else:
result = client.request(
"turn/interrupt",
{
"threadId": thread_id,
"turnId": str(request["expected_turn_id"]),
},
)
if action == "pause":
with state_lock:
state["paused"] = True
state["retire_after_pause"] = bool(
request.get("retire_host", False)
)
state["finalize_requested"] = False
state["finalization_deadline_monotonic"] = None
state["finalization_turn_id"] = None
state["finalization_prompt"] = None
update(
directory,
status="paused",
goal_status=(
"paused" if state["execution_mode"] == "goal" else None
),
finalization_started_at=None,
)
elif state["execution_mode"] == "turn":
with state_lock:
state["paused"] = True
update(directory, status="paused")
elif action in {"continue", "finalize"}:
if action == "finalize":
host = state.get("host")
if state["execution_mode"] == "goal" and isinstance(
host, PersistentThreadHost
):
host.set_goal(status="paused")
finalization_deadline = time.monotonic() + max(
0.0, float(state["finalization_grace_seconds"])
)
with state_lock:
state["finalize_requested"] = True
state["finalization_deadline_monotonic"] = finalization_deadline
state["finalization_turn_id"] = None
state["finalization_prompt"] = str(request["input"])
state["output_schema"] = state.get("terminal_output_schema")
update(
directory,
status="finalizing",
finalization_started_at=utc_now(),
)
action_timeout = (
min(
30.0,
max(0.1, float(state["finalization_grace_seconds"])),
)
if action == "finalize"
else 60.0
)
if action == "continue" and state["execution_mode"] == "goal":
host = state.get("host")
if not isinstance(host, PersistentThreadHost):
raise RuntimeError("worker host is unavailable")
token_budget = request.get("goal_token_budget")
result = {
"goal": host.set_goal(
status="active",
token_budget=(
int(token_budget)
if isinstance(token_budget, int)
and not isinstance(token_budget, bool)
else None
),
)
}
with state_lock:
state["paused"] = False
state.pop("terminal_limit_status", None)
state["finalize_requested"] = False
state["finalization_deadline_monotonic"] = None
state["finalization_turn_id"] = None
state["finalization_prompt"] = None
update(
directory,
status="running",
goal_status="active",
finalization_started_at=None,
)
elif action == "finalize" and state["execution_mode"] == "goal":
if turn_id:
result = client.request(
"turn/interrupt",
{
"threadId": thread_id,
"turnId": str(turn_id),
},
timeout=action_timeout,
)
else:
result = {"finalizing": True}
elif turn_id:
if action != "finalize":
raise RuntimeError("worker already has an active turn")
with state_lock:
state["finalization_turn_id"] = str(turn_id)
result = client.request(
"turn/steer",
{
"threadId": thread_id,
"expectedTurnId": str(turn_id),
"input": _turn_input(str(request["input"]), []),
},
timeout=action_timeout,
)
else:
_start_turn(
client,
state,
str(request["input"]),
[],
state_lock=state_lock,
timeout=min(60.0, action_timeout),
)
with state_lock:
state["paused"] = False
if action == "finalize":
state["finalization_turn_id"] = state.get("active_turn_id")
if action == "continue":
update(
directory,
status="running",
finalization_started_at=None,
)
result = {"continued": True}
elif action == "detach":
update(directory, status="detached", detached_at=utc_now())
result = {"detached": True}
elif action == "stop":
host = state.get("host")
if state["execution_mode"] == "goal" and isinstance(
host, PersistentThreadHost
):
host.set_goal(status="paused")
if turn_id:
with contextlib.suppress(AppServerError):
client.request(
"turn/interrupt",
{"threadId": thread_id, "turnId": str(turn_id)},
)
with state_lock:
state["stop_requested"] = True
result = {"stopping": True}
elif action == "compact":
result = client.request("thread/compact/start", {"threadId": thread_id})
elif action == "respond":
request_id = request["request_id"]
pending_request = next(
(
item
for item in client.pending_server_requests_for_thread(thread_id)
if type(item.get("id")) is type(request_id)
and item.get("id") == request_id
),
None,
)
if pending_request is None:
raise RuntimeError("app-server request is not pending")
method = pending_request.get("method")
if not isinstance(method, str):
raise RuntimeError("pending app-server request has no method")
response = request["response"]
if not isinstance(response, Mapping):
raise ValueError("app-server response must be an object")
validate_server_request_response(method, response)
client.respond(request_id, response)
pending_count = len(client.pending_server_requests_for_thread(thread_id))
result = {"responded": True, "request_id": request_id}
update(
directory,
status="waiting" if pending_count else "running",
pending_request_count=pending_count,
)
elif action == "set_effort":
effort = str(request["effort"])
result = client.request(
"thread/settings/update",
{"threadId": thread_id, "effort": effort},
)
with state_lock:
state["current_effort"] = effort
update(directory, reasoning_effort=effort)
else:
raise ValueError(f"unsupported worker control action: {action}")
send_control_response(connection, {"ok": True, "result": result})
except Exception as exc:
# The controller may abandon a bounded request while the
# app server is still replying. A broken response socket
# must not terminate this worker's control thread.
with contextlib.suppress(OSError):
send_control_response(
connection,
{"ok": False, "error": f"{type(exc).__name__}: {exc}"},
)
def _start_turn(
client: AppServerClient,
state: dict[str, Any],
prompt: str,
attachments: list[str],
*,
state_lock: threading.RLock,
timeout: float = 60.0,
) -> str:
"""Bridge existing worker control paths onto the shared persistent host."""
host = state.get("host")
if not isinstance(host, PersistentThreadHost):
raise AppServerError("worker persistent thread host is unavailable")
if host.client is not client or host.state_lock is not state_lock:
raise AppServerError("worker persistent thread host identity changed")
return host.start_turn(
_turn_input(prompt, attachments),
effort=state.get("current_effort"),
output_schema=state.get("output_schema"),
timeout=timeout,
)
def _advance_finalization(
*,
host: PersistentThreadHost,
state: dict[str, Any],
state_lock: threading.RLock,
completed_turn: Mapping[str, Any] | None,
execution_mode: str,
terminal_output_schema: Mapping[str, Any] | None,
lifecycle_timeout: float,
) -> tuple[str, str, int, float | None]:
"""Advance one host-timed finalization step without imposing a task clock."""
with state_lock:
requested = bool(state.get("finalize_requested"))
raw_deadline = state.get("finalization_deadline_monotonic")
finalization_turn_id = state.get("finalization_turn_id")
active_turn_id = state.get("active_turn_id")
finalization_prompt = state.get("finalization_prompt")
if not requested:
return "inactive", "", 0, None
deadline = (
float(raw_deadline)
if isinstance(raw_deadline, (int, float)) and not isinstance(raw_deadline, bool)
else time.monotonic()
)
if isinstance(completed_turn, Mapping) and (
completed_turn.get("id") == finalization_turn_id
or (execution_mode == "turn" and finalization_turn_id is None)
):
return (
"completed",
_last_agent_message(completed_turn),
1 if completed_turn.get("status") == "failed" else 0,
deadline,
)
remaining = deadline - time.monotonic()
if remaining <= 0:
return "expired", "", 0, deadline
if execution_mode == "goal" and finalization_turn_id is None and active_turn_id is None:
new_turn_id = host.start_turn(
_turn_input(
str(finalization_prompt)
if isinstance(finalization_prompt, str) and finalization_prompt.strip()
else "Terminal serialization turn. Do not investigate or call tools. Return the "
"best supported final result using only evidence already present in this thread.",
[],
),
effort=state.get("current_effort"),
output_schema=terminal_output_schema,
timeout=min(lifecycle_timeout, max(0.1, remaining)),
)
with state_lock:
state["finalization_turn_id"] = new_turn_id
state["paused"] = False
return "waiting", "", 0, deadline
def _fail(
directory: Path,
exit_code: int,
message: str,
*,
runner: WorkerRunner | None = None,
warning: str | None = None,
usage: dict[str, int] | None = None,
) -> int:
partial = _partial_result(directory, reason=message)
changes: dict[str, Any] = {
"status": "failed",
"exit_code": exit_code,
"error": message,
"warning": warning or message,
}
if usage is not None:
changes["usage"] = usage
changes.update(partial)
_current, cancelled = finalize(
directory,
runner=runner,
**changes,
)
return 130 if cancelled else shell_exit_status(exit_code or 1)
def _cancelled_worker_exit(runner: WorkerRunner, started: float) -> int:
directory = runner.directory
runner.terminate_child()
partial = _partial_result(directory, reason="worker cancelled")
finalize(
directory,
runner=runner,
status="cancelled",
elapsed_seconds=time.monotonic() - started,
**partial,
)
return 130
def _file_not_found_exit(
runner: WorkerRunner,
state: Mapping[str, Any],
codex_bin: str,
error: FileNotFoundError,
) -> int:
directory = runner.directory
runner.terminate_child()
_settle_recovery_control(
directory,
state,
status=("delivery_unknown" if state.get("recovery_control_dispatched") else "failed"),
error=f"{type(error).__name__}: {error}",
)
if _has_durable_thread_work(state):
partial = _partial_result(
directory,
reason=f"Codex executable unavailable during durable-thread hosting: {error}",
)
update(
directory,
status="suspended",
error=f"unable to execute Codex binary {codex_bin!r}: {error}",
**partial,
)
return 75
return _fail(
directory,
127,
f"unable to execute Codex binary {codex_bin!r}: {error}",
runner=runner,
)
def _app_server_error_exit(
runner: WorkerRunner,
state: Mapping[str, Any],
started: float,
error: AppServerError,
) -> int:
directory = runner.directory
if runner.stop_requested:
return _cancelled_worker_exit(runner, started)
runner.terminate_child()
recovery_status = "delivery_unknown" if state.get("recovery_control_dispatched") else "failed"
_settle_recovery_control(
directory,
state,
status=recovery_status,
error=f"{type(error).__name__}: {error}",
)
if _has_durable_thread_work(state):
partial = _partial_result(directory, reason=f"app-server host suspended: {error}")
update(
directory,
status="suspended",
error=f"app-server host suspended: {error}",
**partial,
)
return 75
return _fail(directory, 1, f"app-server startup failed: {error}", runner=runner)
def _has_durable_thread_work(state: Mapping[str, Any]) -> bool:
"""Distinguish useful persisted work from a newly allocated empty thread."""
return bool(
isinstance(state.get("active_turn_id"), str)
or isinstance(state.get("last_turn_id"), str)
or state.get("turn_start_pending") is True
or isinstance(state.get("completed_turn"), Mapping)
or isinstance(state.get("goal"), Mapping)
)
def _repair_strict_contract(
*,
runner: WorkerRunner | None = None,
directory: Path,
state: dict[str, Any],
state_lock: threading.RLock,
metadata: Mapping[str, Any],
output_contract: Any,
result_text: str,
result_path: Path,
deadline: float | None = None,
) -> str:
"""Attempt one same-thread schema repair and retain the original on timeout."""
if output_contract is None or metadata.get("contract_enforcement") != "strict":
return result_text
structured_preview, extraction_error = extract_json_document(result_text)
preview_errors = (
[extraction_error]
if extraction_error
else validate_instance(structured_preview, output_contract)
)
if not preview_errors:
return result_text
repair_deadline = deadline or (time.monotonic() + int(metadata["finalization_grace_seconds"]))
remaining = repair_deadline - time.monotonic()
if remaining <= 0:
return result_text
update(directory, status="finalizing", contract_repair_attempted=True)
repair_prompt = (
"Repair only the final JSON result. Do not call tools or add prose. "
"Return exactly one document satisfying this schema:\n"
+ json.dumps(
output_contract,
ensure_ascii=False,
indent=2,
sort_keys=True,
allow_nan=False,
)
+ "\n\nValidation errors:\n- "
+ "\n- ".join(preview_errors[:20])
+ "\n\nInvalid result:\n"
+ result_text[:100000]
)
client = state.get("client")
if not isinstance(client, AppServerClient):
raise AppServerError("app-server client disappeared before contract repair")
try:
_start_turn(
client,
state,
repair_prompt,
[],
state_lock=state_lock,
timeout=min(60.0, max(0.1, remaining)),
)
except AppServerError:
if time.monotonic() >= repair_deadline:
return result_text
raise
while time.monotonic() < repair_deadline:
if runner is not None and runner.stop_requested:
raise KeyboardInterrupt
if state["turn_event"].wait(timeout=0.25):
with state_lock:
repaired_turn = state.get("completed_turn")
state["turn_event"].clear()
repaired = _last_agent_message(repaired_turn)
if repaired.strip():
result_path.write_text(repaired, encoding="utf-8")
return repaired
return result_text
if not client.alive:
raise AppServerError("app-server transport closed during contract repair")
return result_text
def _lifecycle_status_for_goal(goal_status: Any) -> str | None:
"""Translate Codex goal state into the durable worker lifecycle."""
if goal_status == "active":
return "running"
if goal_status in {"paused", "blocked"}:
return "paused"
if goal_status in {"usageLimited", "budgetLimited"}:
return "suspended"
return None
def _goal_objective_from_metadata(metadata: Mapping[str, Any]) -> str:
value = metadata.get("goal_objective")
if isinstance(value, str) and value.strip():
return value
return bounded_goal_objective(str(metadata["task"]))
def _persist_worker_host_state(
directory: Path,
state: Mapping[str, Any],
changes: Mapping[str, Any],
) -> None:
persisted: dict[str, Any] = {"last_progress_at": utc_now(), **changes}
current_status = str(_read_metadata(directory).get("status"))
status_is_protected = current_status in (
_TERMINAL_OR_CANCELLING_STATUSES | {"detached", "paused", "recovering", "finalizing"}
)
goal = changes.get("goal")
if isinstance(goal, Mapping):
persisted.update(
goal_status=goal.get("status"),
goal_objective=goal.get("objective"),
goal_token_budget=goal.get("tokenBudget"),
goal_tokens_used=goal.get("tokensUsed", 0),
goal_time_used_seconds=goal.get("timeUsedSeconds", 0),
)
goal_lifecycle = _lifecycle_status_for_goal(goal.get("status"))
if goal_lifecycle == "suspended" and isinstance(state.get("active_turn_id"), str):
# A limit notification can precede turn/completed. The admitted
# turn still owns its scheduler and write leases until its terminal
# event is consumed or the host is suspended explicitly.
goal_lifecycle = "running"
if goal_lifecycle is not None and not status_is_protected:
persisted["status"] = goal_lifecycle
if "turn_failure" in changes:
persisted["failure"] = changes["turn_failure"]
if changes["turn_failure"] is None:
persisted["error"] = None
if "pending_request_count" in changes and not status_is_protected:
persisted["status"] = (
"waiting"
if int(changes["pending_request_count"]) > 0
else "running"
if current_status == "waiting"
else persisted.get("status", current_status)
)
elif not status_is_protected and isinstance(changes.get("active_turn_id"), str):
persisted["status"] = "finalizing" if state.get("finalize_requested") else "running"
update(directory, **persisted)
def _publish_worker_terminal(
*,
runner: WorkerRunner,
metadata: dict[str, Any],
session: Mapping[str, Any],
state: Mapping[str, Any],
control_stop: threading.Event,
control_thread: threading.Thread | None,
started: float,
exit_code: int,
) -> int:
"""Validate and publish the terminal result after the app-server loop ends."""
directory = runner.directory
cwd = Path(str(metadata["cwd"])).resolve()
result_path = directory / "result.md"
events_path = directory / "events.jsonl"
stderr_path = directory / "stderr.log"
control_stop.set()
if control_thread is not None:
with contextlib.suppress(Exception):
control_thread.join(timeout=2.0)
runner.terminate_child()
runner.app_server = None
elapsed = time.monotonic() - started
usage = _extract_usage(events_path)
observed_route = route_telemetry(metadata, session, events_path)
if runner.stop_requested or _read_metadata(directory).get("status") == "cancelling":
partial = _partial_result(directory, reason="worker cancelled")
finalize(
directory,
runner=runner,
status="cancelled",
elapsed_seconds=elapsed,
exit_code=exit_code,
usage=usage,
**partial,
)
return 130
warnings = route_telemetry_warnings(metadata, observed_route)
artifacts: list[dict[str, Any]] = []
patch: dict[str, Any] | None = None
if metadata["sandbox_mode"] == "workspace-write":
boundary_errors, artifacts, patch = capture_isolated_patch(metadata, directory, cwd)
if boundary_errors:
reason = "; ".join(boundary_errors)
taint_session(str(metadata["session_id"]), reason, job_id=str(metadata["job_id"]))
return _fail(
directory,
1,
"writable worker violated its mechanically enforced boundary: " + reason,
runner=runner,
usage=usage,
)
result_text = (
result_path.read_text(encoding="utf-8", errors="replace") if result_path.is_file() else ""
)
contract_valid: bool | None = None
contract_errors: list[str] = []
structured: Any = None
if metadata.get("output_contract") is not None:
structured, contract_valid, contract_errors, declared_artifacts = _validate_result_contract(
result_text,
metadata=metadata,
cwd=cwd,
events_path=events_path,
)
known_artifacts = {(item.get("relative_path"), item.get("sha256")) for item in artifacts}
artifacts.extend(
item
for item in declared_artifacts
if (item.get("relative_path"), item.get("sha256")) not in known_artifacts
)
if contract_errors:
warnings.append("output contract violation: " + "; ".join(contract_errors[:12]))
terminal_limit_status = state.get("terminal_limit_status")
if terminal_limit_status in {"usageLimited", "budgetLimited"} and contract_valid is True:
warnings.append(
f"goal reached {terminal_limit_status} after producing a fully validated strict "
"result; exact observed usage is retained"
)
current_status = str(_read_metadata(directory).get("status"))
if current_status == "stopped":
partial = _partial_result(directory, reason="worker stopped by controller")
final, cancelled = finalize(
directory,
runner=runner,
status="stopped",
elapsed_seconds=elapsed,
exit_code=1,
usage=usage,
**partial,
)
return 130 if cancelled else shell_exit_status(int(final.get("exit_code", 1)))
if exit_code != 0 or not result_text.strip():
stderr_tail = (
stderr_path.read_text(encoding="utf-8", errors="replace")[-5000:]
if stderr_path.is_file()
else ""
)
message = "Codex worker failed" if exit_code else "Codex worker produced no final result"
if stderr_tail:
message += ": " + stderr_tail
return _fail(
directory,
int(exit_code or 1),
message,
runner=runner,
warning="; ".join(warnings) or None,
usage=usage,
)
strict_contract_failure = (
contract_valid is False and metadata.get("contract_enforcement", "warn") == "strict"
)
status = (
"failed"
if strict_contract_failure
else "completed_with_warnings"
if warnings
else "completed"
)
changes: dict[str, Any] = {
"status": status,
"finished_at": utc_now(),
"elapsed_seconds": elapsed,
"exit_code": exit_code,
"usage": usage,
"route_telemetry": observed_route,
"contract_valid": contract_valid,
"contract_errors": contract_errors,
"warning": "; ".join(warnings) or None,
"artifacts": artifacts,
"patch": patch,
"goal_status": (
state.get("terminal_limit_status")
or (
state.get("goal", {}).get("status")
if isinstance(state.get("goal"), Mapping)
else None
)
),
"result_kind": "final",
"result_state": "unread",
"failure": None,
"error": None,
}
if structured is not None and contract_valid:
structured_path = directory / "result.json"
atomic_write_json(structured_path, structured)
changes["structured_result_path"] = str(structured_path)
if strict_contract_failure:
changes["error"] = "worker output did not satisfy its required result contract"
final, cancelled = finalize(directory, runner=runner, **changes)
final_status = str(final["status"])
append_audit(
str(session["session_id"]),
"agent_completed",
run_id=metadata["run_id"],
job_id=metadata["job_id"],
status=final_status,
exit_code=exit_code,
contract_valid=contract_valid,
elapsed_seconds=elapsed,
route_telemetry=observed_route,
)
return 130 if cancelled else shell_exit_status(int(final.get("exit_code", exit_code)))
def _run_worker_event_loop(
*,
runner: WorkerRunner,
directory: Path,
metadata: Mapping[str, Any],
session: Mapping[str, Any],
state: dict[str, Any],
state_lock: threading.RLock,
host: PersistentThreadHost,
connect_app_server: Callable[..., AppServerClient],
resume_recovered_work: Callable[[AppServerClient, str], None],
latest_history_result: Callable[[], str],
execution_mode: str,
terminal_output_schema: dict[str, Any] | None,
lifecycle_timeout: float,
control_failed: threading.Event,
control_errors: list[BaseException],
control_stop: threading.Event,
progress: dict[str, Any],
cwd: Path,
events_path: Path,
initial_result_text: str,
) -> tuple[str, int, float | None, bool, int | None]:
"""Drive one worker until a durable terminal result or recoverable suspension."""
result_text = initial_result_text
exit_code = 0
contract_repair_deadline: float | None = None
finalization_expired = False
last_heartbeat = 0.0
recovery_attempts = int(metadata.get("recovery_attempts", 0))
stall_seconds = int(metadata["stall_warning_seconds"])
if result_text:
return result_text, exit_code, contract_repair_deadline, finalization_expired, None
while not runner.stop_requested:
now = time.monotonic()
if control_failed.is_set():
error = control_errors[0]
raise AppServerError(
f"worker control socket failed: {type(error).__name__}: {error}"
) from error
if now - last_heartbeat >= 15.0:
goal = state.get("goal")
heartbeat: dict[str, Any] = {
"heartbeat_at": utc_now(),
"pending_request_count": len(
state["client"].pending_server_requests_for_thread(str(state["thread_id"]))
),
}
if isinstance(goal, Mapping):
heartbeat.update(
goal_status=goal.get("status"),
goal_tokens_used=goal.get("tokensUsed", 0),
goal_time_used_seconds=goal.get("timeUsedSeconds", 0),
)
update(directory, **heartbeat)
last_heartbeat = now
if not progress["stall_reported"] and now - progress["last_progress"] >= stall_seconds:
progress["stall_reported"] = True
update(directory, stall_warning_at=utc_now())
append_audit(
str(session["session_id"]),
"agent_stall_warning",
run_id=metadata["run_id"],
job_id=metadata["job_id"],
warning_seconds=stall_seconds,
)
if state.get("stop_requested"):
update(directory, status="stopped")
break
completed_turn: dict[str, Any] | None = None
paused = False
if state["turn_event"].wait(timeout=0.25):
with state_lock:
completed_turn = state.get("completed_turn")
state["completed_turn"] = None
state["turn_event"].clear()
paused = bool(state.get("paused"))
if runner.stop_requested:
break
if state.get("retire_after_pause"):
partial = _partial_result(directory, reason="worker cold-paused by controller")
update(
directory,
status="paused",
paused_at=utc_now(),
active_turn_id=None,
goal_status=("paused" if execution_mode == "goal" else None),
**partial,
)
control_stop.set()
state["client"].close()
runner.app_server = None
return "", exit_code, contract_repair_deadline, finalization_expired, 75
# Recover the transport before interpreting any turn or goal state. A
# completion/limit notification can arrive just before the socket closes
# while active_turn_id still names a turn already persisted by Codex.
# Finalization's waiting state must not mask that dead client either.
if not state["client"].alive:
recovered = False
last_recovery_error = "app-server transport closed"
pre_recovery_status = str(_read_metadata(directory).get("status"))
for incident_attempt, delay in enumerate(
APP_SERVER_RECOVERY_DELAYS_SECONDS,
start=1,
):
if runner.stop_requested:
break
previous = state.get("client")
if isinstance(previous, AppServerClient):
previous.close()
recovery_attempts += 1
update(
directory,
status="recovering",
recovery_attempts=recovery_attempts,
recovery_incident_attempt=incident_attempt,
)
deadline = time.monotonic() + delay
while time.monotonic() < deadline and not runner.stop_requested:
time.sleep(min(0.25, deadline - time.monotonic()))
if runner.stop_requested:
break
try:
recovered_client = connect_app_server(resume=True)
runner.app_server = recovered_client
with state_lock:
state["client"] = recovered_client
resume_recovered_work(recovered_client, pre_recovery_status)
update(
directory,
status=(
pre_recovery_status
if pre_recovery_status
in {"paused", "detached", "waiting", "finalizing"}
else "running"
),
recovery_error=None,
recovery_incident_attempt=0,
)
recovered = True
break
except (AppServerError, FileNotFoundError, OSError) as exc:
last_recovery_error = f"{type(exc).__name__}: {exc}"
update(directory, recovery_error=last_recovery_error)
if not recovered and not runner.stop_requested:
partial = _partial_result(
directory,
reason="app-server recovery attempts exhausted",
)
update(
directory,
status="suspended",
error="app-server recovery attempts exhausted: " + last_recovery_error,
**partial,
)
control_stop.set()
runner.app_server = None
return "", exit_code, contract_repair_deadline, finalization_expired, 75
finalization_state, finalization_result, finalization_exit, finalization_deadline = (
_advance_finalization(
host=host,
state=state,
state_lock=state_lock,
completed_turn=completed_turn,
execution_mode=execution_mode,
terminal_output_schema=terminal_output_schema,
lifecycle_timeout=lifecycle_timeout,
)
)
if finalization_state == "completed":
result_text = finalization_result
exit_code = finalization_exit
contract_repair_deadline = finalization_deadline
break
if finalization_state == "expired":
contract_repair_deadline = finalization_deadline
finalization_expired = True
update(
directory,
status="stopped",
finalization_expired_at=utc_now(),
)
break
if finalization_state == "waiting":
continue
if (
isinstance(completed_turn, Mapping)
and completed_turn.get("status") == "interrupted"
and paused
):
update(directory, status="paused", active_turn_id=None)
elif isinstance(completed_turn, Mapping) and completed_turn.get("status") == "failed":
failure = normalize_turn_failure(completed_turn) or {
"kind": "turn_failed",
"source": "turn/completed",
"turn_id": completed_turn.get("id"),
"turn_status": "failed",
"message": "app-server turn failed",
"retryable": False,
"observed_at": utc_now(),
}
partial = _partial_result(directory, reason=str(failure["message"]))
status = "suspended" if failure.get("retryable") else "failed"
update(
directory,
status=status,
suspended_at=utc_now() if status == "suspended" else None,
finished_at=utc_now() if status == "failed" else None,
error=str(failure["message"]),
failure=failure,
**partial,
)
control_stop.set()
runner.terminate_child()
runner.app_server = None
return (
"",
exit_code,
contract_repair_deadline,
finalization_expired,
75 if status == "suspended" else 1,
)
elif execution_mode == "turn" and isinstance(completed_turn, dict):
if completed_turn.get("status") == "failed":
exit_code = 1
result_text = _last_agent_message(completed_turn)
break
goal = state.get("goal")
if execution_mode == "goal" and isinstance(goal, Mapping):
goal_status = goal.get("status")
if goal_status == "complete":
# update_goal can make the goal complete before Codex emits
# turn/completed and the terminal agent message. Preserve
# that active turn before reading history or beginning a
# strict terminal-serialization turn.
if state.get("active_turn_id") is not None:
continue
result_text = latest_history_result()
if terminal_output_schema is not None:
finalization_deadline = time.monotonic() + int(
metadata["finalization_grace_seconds"]
)
with state_lock:
state["output_schema"] = terminal_output_schema
state["finalize_requested"] = True
state["finalization_deadline_monotonic"] = finalization_deadline
state["finalization_turn_id"] = None
state["finalization_prompt"] = (
"Terminal serialization turn. Do not investigate or call tools. "
"Return exactly one JSON document satisfying the compiled output "
"schema, using only evidence already present in this thread."
)
update(
directory,
status="finalizing",
finalization_started_at=utc_now(),
)
continue
break
if goal_status in {"usageLimited", "budgetLimited"}:
state["terminal_limit_status"] = goal_status
# A goal-limit notification may precede turn/completed by
# only milliseconds. Never interrupt or discard that
# already-admitted turn; classify its terminal message
# after the app server declares the turn complete.
if state.get("active_turn_id") is not None:
continue
terminal_turn: Any = completed_turn
if not isinstance(terminal_turn, Mapping):
turns = host.complete_history(timeout=lifecycle_timeout)
terminal_turn = turns[-1] if turns else None
candidate = _successful_current_turn_result(
terminal_turn,
state.get("last_turn_id"),
)
if _strict_result_candidate_is_valid(
candidate,
metadata=metadata,
cwd=cwd,
events_path=events_path,
):
result_text = candidate
break
partial = _partial_result(
directory,
reason=f"goal suspended with status {goal_status}",
)
update(
directory,
status="suspended",
error=f"goal suspended with status {goal_status}",
**partial,
)
control_stop.set()
state["client"].close()
runner.app_server = None
return "", exit_code, contract_repair_deadline, finalization_expired, 75
return result_text, exit_code, contract_repair_deadline, finalization_expired, None
def _run_worker(runner: WorkerRunner) -> int:
directory = runner.directory
metadata = _read_metadata(directory)
if metadata.get("worktree_root"):
atexit.register(_cleanup_worktree_on_exit, directory)
caller_token = os.environ.get("MMO_CALLER_TOKEN", "")
if not caller_token:
return _fail(
directory,
2,
"worker caller capability is missing from launch environment",
runner=runner,
)
session = read_session_record(session_dir(str(metadata["session_id"])))
if session.get("snapshot_hash") != metadata.get("snapshot_hash"):
return _fail(directory, 2, "job/session snapshot mismatch", runner=runner)
if session.get("current_run_id") != metadata.get("run_id"):
return _fail(directory, 2, "job belongs to an inactive session run", runner=runner)
agent_id = str(metadata["agent"])
home = session["homes"][agent_id]
cwd = Path(str(metadata["cwd"])).resolve()
prompt_path = directory / "prompt.txt"
result_path = directory / "result.md"
events_path = directory / "events.jsonl"
stderr_path = directory / "stderr.log"
socket_path = Path(str(metadata["app_server_socket_path"]))
expected_paths = {
"result_path": result_path,
"events_path": events_path,
"stderr_path": stderr_path,
}
unsafe_paths = [
key
for key, path in expected_paths.items()
if metadata.get(key) != str(path) or path.is_symlink()
]
if prompt_path.is_symlink() or not prompt_path.is_file():
unsafe_paths.append("prompt_path")
if not socket_path.is_absolute() or socket_path.is_symlink():
unsafe_paths.append("app_server_socket_path")
if unsafe_paths:
return _fail(
directory,
2,
"worker state contains unsafe artifact paths: " + ", ".join(unsafe_paths),
runner=runner,
)
runner.install_signal_handlers()
if begin_running(directory, runner=runner) is None:
append_audit(
session["session_id"],
"agent_cancelled_terminal",
run_id=metadata["run_id"],
job_id=metadata["job_id"],
phase="before_codex_launch",
)
return 130
start_event = (
"agent_resumed"
if metadata.get("status") == "recovering"
or isinstance(metadata.get("app_server_thread_id"), str)
else "agent_started"
)
append_audit(
session["session_id"],
start_event,
run_id=metadata["run_id"],
job_id=metadata["job_id"],
agent=agent_id,
parent_job_id=metadata.get("parent_job_id"),
)
codex_bin = str(session["codex_binary"])
command = app_server_listen_command(
codex_bin,
home.get("command_flags", []),
socket_path,
)
output_contract = metadata.get("output_contract")
terminal_output_schema = (
_codex_output_schema(output_contract)
if isinstance(output_contract, dict)
and metadata.get("contract_enforcement") == "strict"
and metadata.get("structured_output_supported", False)
else None
)
contract_transport = (
"validated_text"
if terminal_output_schema is None
else "native_schema"
if terminal_output_schema == output_contract
else "native_schema_projection"
)
update(directory, command=command, contract_transport=contract_transport)
if runner.stop_requested or _read_metadata(directory).get("status") == "cancelling":
finalize(
directory,
runner=runner,
status="cancelled",
warning="worker launch was cancelled before Codex started",
)
return 130
started = time.monotonic()
state_lock = threading.RLock()
control_stop = threading.Event()
execution_mode = str(metadata["execution_mode"])
recovery_finalize = bool(
metadata.get("finalize_requested_on_recovery")
or metadata.get("recovery_action") == "finalize"
)
state: dict[str, Any] = {
"client": None,
"thread_id": metadata.get("app_server_thread_id"),
"active_turn_id": metadata.get("active_turn_id"),
"last_turn_id": metadata.get("last_turn_id"),
"turn_start_pending": bool(metadata.get("turn_start_pending", False)),
"completed_turn": None,
"turn_event": threading.Event(),
"thread_status": metadata.get("thread_status"),
"goal": None,
"paused": metadata.get("status") == "paused",
"stop_requested": False,
"finalize_requested": recovery_finalize,
"finalization_deadline_monotonic": (
started + int(metadata["finalization_grace_seconds"]) if recovery_finalize else None
),
"finalization_turn_id": None,
"finalization_prompt": (metadata.get("recovery_prompt") if recovery_finalize else None),
"current_effort": metadata.get("reasoning_effort"),
"finalization_grace_seconds": int(metadata["finalization_grace_seconds"]),
"execution_mode": execution_mode,
"agent_run_ref": metadata["agent_run_ref"],
"recovery_action": metadata.get("recovery_action"),
"recovery_control_revision": metadata.get("recovery_control_revision"),
"recovery_controller_agent": metadata.get("recovery_controller_agent"),
"recovery_controller_job_id": metadata.get("recovery_controller_job_id"),
"recovery_control_dispatched": False,
"output_schema": (
terminal_output_schema if execution_mode == "turn" or recovery_finalize else None
),
"terminal_output_schema": terminal_output_schema,
"retire_after_pause": False,
}
lifecycle_timeout = float(
metadata.get(
"app_server_lifecycle_timeout_seconds",
APP_SERVER_LIFECYCLE_TIMEOUT_SECONDS,
)
)
progress: dict[str, Any] = {
"last_progress": time.monotonic(),
"stall_reported": False,
}
def persist_host_state(changes: dict[str, Any], _message: Mapping[str, Any]) -> None:
progress["last_progress"] = time.monotonic()
progress["stall_reported"] = False
_persist_worker_host_state(directory, state, changes)
host = PersistentThreadHost(
state=state,
state_lock=state_lock,
on_state_change=persist_host_state,
)
state["host"] = host
environment = session_environment(
session,
agent_id,
caller_job_id=metadata["job_id"],
caller_token=caller_token,
)
def connect_app_server(*, resume: bool) -> AppServerClient:
with state_lock:
prior_turn_id = state.get("last_turn_id") or state.get("active_turn_id")
observed = _read_metadata(directory)
host_alive = process_matches(
observed.get("app_server_pid"),
observed.get("app_server_start_token"),
)
if not host_alive:
# Worker hosts can be recreated long after session admission. Do
# not let an executable replaced in place silently change the
# experimental app-server protocol on the persisted thread.
require_app_server_codex_version(codex_bin)
if not host_alive and (socket_path.exists() or socket_path.is_symlink()):
if socket_path.is_symlink() or not socket_path.is_socket():
raise AppServerError("recorded worker app-server socket is unsafe")
socket_path.unlink()
client = AppServerClient(
socket_path=socket_path,
command=None if host_alive else command,
cwd=cwd,
env=environment,
events_path=events_path,
stderr_path=stderr_path,
approval_policy=str(metadata["approval_policy"]),
on_message=host.on_message,
)
def publish_host(pid: int) -> None:
start_token = process_start_token(pid)
if start_token is None:
raise AppServerError("worker app-server process could not be fingerprinted")
with file_lock(runtime_lock_path()):
current = _read_metadata(directory)
if current.get("status") in _TERMINAL_OR_CANCELLING_STATUSES:
raise AppServerError("worker admission closed during app-server bootstrap")
current.update(
app_server_pid=pid,
app_server_pgid=pid,
app_server_start_token=start_token,
app_server_process_group_isolated=True,
app_server_protocol="codex-app-server-v2-unix",
)
publish_job_record(directory, current)
runner.app_server = client
try:
client.start(
timeout=APP_SERVER_INITIALIZE_TIMEOUT_SECONDS,
on_started=publish_host if not host_alive else None,
)
host.attach_client(client)
dynamic_tools: list[dict[str, Any]] = []
if metadata["driver"] == "switchyard":
observed_switchyard = session.get("switchyard_version")
if not isinstance(observed_switchyard, str):
raise AppServerError("Switchyard session has no pinned gateway version")
if observed_switchyard == SWITCHYARD_MCP_NAMESPACE_BRIDGE_VERSION:
dynamic_tools = client.install_switchyard_mcp_bridge(timeout=lifecycle_timeout)
expected_thread_id: str | None = None
if resume:
mode = "resume"
expected_thread_id = str(state["thread_id"])
thread_params: dict[str, Any] = {
"threadId": expected_thread_id,
"cwd": str(cwd),
"sandbox": metadata["sandbox_mode"],
"approvalPolicy": metadata["approval_policy"],
"excludeTurns": False,
}
elif metadata.get("fork_thread_id"):
mode = "fork"
thread_params = {
"threadId": str(metadata["fork_thread_id"]),
"cwd": str(cwd),
"sandbox": metadata["sandbox_mode"],
"approvalPolicy": metadata["approval_policy"],
"ephemeral": False,
"deferGoalContinuation": True,
"excludeTurns": False,
}
else:
mode = "start"
thread_params = {
"cwd": str(cwd),
"sandbox": metadata["sandbox_mode"],
"approvalPolicy": metadata["approval_policy"],
"allowProviderModelFallback": False,
"ephemeral": False,
"historyMode": "paginated",
}
if dynamic_tools:
thread_params["dynamicTools"] = dynamic_tools
thread = host.open_thread(
mode,
thread_params,
timeout=lifecycle_timeout,
prior_turn_id=prior_turn_id,
expected_thread_id=expected_thread_id,
interrupt_stale=bool(resume and not host_alive and execution_mode == "turn"),
)
update(
directory,
app_server_thread_id=thread["id"],
active_turn_id=state.get("active_turn_id"),
last_turn_id=state.get("last_turn_id"),
turn_start_pending=bool(state.get("turn_start_pending")),
app_server_protocol="codex-app-server-v2-unix",
)
return client
except BaseException:
host.detach_client(client)
if client.process is not None:
client.stop_host()
else:
client.close()
if runner.app_server is client:
runner.app_server = None
raise
def latest_history_result() -> str:
turns = host.complete_history(timeout=lifecycle_timeout)
for turn in reversed(turns):
result = _last_agent_message(turn)
if result.strip():
return result
return ""
control_thread: threading.Thread | None = None
exit_code = 0
result_text = ""
contract_repair_deadline: float | None = None
finalization_expired = False
try:
resuming_thread = bool(state["thread_id"])
active_client = connect_app_server(resume=resuming_thread)
runner.app_server = active_client
control_ready = threading.Event()
control_failed = threading.Event()
control_errors: list[BaseException] = []
def serve_control() -> None:
try:
_serve_control(
directory,
state,
state_lock,
control_stop,
control_ready,
)
except BaseException as exc:
control_errors.append(exc)
control_failed.set()
control_ready.set()
control_thread = threading.Thread(
target=serve_control,
name=f"mmo-control-{metadata['job_id']}",
daemon=True,
)
control_thread.start()
if not control_ready.wait(timeout=APP_SERVER_INITIALIZE_TIMEOUT_SECONDS):
raise AppServerError("worker control socket did not become ready")
if control_errors:
error = control_errors[0]
raise AppServerError(
f"worker control socket startup failed: {type(error).__name__}: {error}"
) from error
prompt = prompt_path.read_text(encoding="utf-8")
def resume_recovered_work(client: AppServerClient, prior_status: str) -> None:
if execution_mode == "goal" and not isinstance(state.get("goal"), Mapping):
observed = _read_metadata(directory)
persisted_goal_status = observed.get("goal_status")
if persisted_goal_status in {
"complete",
"paused",
"blocked",
"usageLimited",
"budgetLimited",
}:
# Some hosts can resume a terminal persisted turn without
# returning its former goal object. MMO's durable goal
# record still owns lifecycle classification for that exact
# thread; restoring it avoids duplicating completed work.
with state_lock:
state["goal"] = {
"objective": observed.get("goal_objective"),
"status": persisted_goal_status,
"tokenBudget": observed.get("goal_token_budget"),
"tokensUsed": observed.get("goal_tokens_used", 0),
"timeUsedSeconds": observed.get("goal_time_used_seconds", 0),
}
if prior_status in {"paused", "waiting", "finalizing"}:
return
if (
execution_mode == "turn"
and state.get("active_turn_id") is None
and state.get("completed_turn") is None
):
_start_turn(
client,
state,
"Continue the original delegated task from the persisted app-server thread "
"after transport recovery. Use retained evidence and satisfy the original "
"result contract.",
[],
state_lock=state_lock,
)
elif execution_mode == "goal" and not isinstance(state.get("goal"), Mapping):
host.set_goal(
objective=_goal_objective_from_metadata(metadata),
status="paused",
token_budget=int(metadata["goal_token_budget"]),
timeout=lifecycle_timeout,
)
if state.get("active_turn_id") is None and state.get("completed_turn") is None:
host.start_turn(
_turn_input(
"Continue the original delegated objective from the persisted "
"app-server thread after transport recovery.",
[],
),
effort=state.get("current_effort"),
)
host.set_goal(status="active", timeout=lifecycle_timeout)
if recovery_finalize and metadata.get("goal_status") in {
"usageLimited",
"budgetLimited",
}:
state["terminal_limit_status"] = metadata["goal_status"]
if recovery_finalize:
turns = host.complete_history(timeout=lifecycle_timeout)
candidate = _successful_current_turn_result(
turns[-1] if turns else None,
state.get("last_turn_id"),
)
if _strict_result_candidate_is_valid(
candidate,
metadata=metadata,
cwd=cwd,
events_path=events_path,
):
result_text = candidate
if state.get("recovery_action") in {"continue", "finalize"}:
# From here onward the requested action is either accepted locally
# from exact-thread history or may send a mutating app-server call.
# A transport loss is therefore uncertain rather than safely failed.
state["recovery_control_dispatched"] = True
if result_text:
pass
elif execution_mode == "goal":
existing_goal = state.get("goal")
if not isinstance(existing_goal, Mapping):
host.set_goal(
objective=_goal_objective_from_metadata(metadata),
status="paused",
token_budget=int(metadata["goal_token_budget"]),
timeout=lifecycle_timeout,
)
if not recovery_finalize and state.get("active_turn_id") is None:
host.start_turn(
_turn_input(prompt, list(metadata.get("attachments", []))),
effort=state.get("current_effort"),
output_schema=None,
)
if not recovery_finalize:
active_goal = host.set_goal(status="active", timeout=lifecycle_timeout)
update(
directory,
status="running",
goal_status=active_goal.get("status"),
)
elif existing_goal.get("status") in {
"paused",
"blocked",
"usageLimited",
"budgetLimited",
} and metadata.get("continue_requested"):
active_goal = host.set_goal(status="active", timeout=lifecycle_timeout)
update(
directory,
status="running",
goal_status=active_goal.get("status"),
continue_requested=False,
)
elif recovery_finalize and existing_goal.get("status") != "paused":
host.set_goal(status="paused", timeout=lifecycle_timeout)
elif state.get("active_turn_id") is None and state.get("completed_turn") is None:
recovery_prompt = metadata.get("recovery_prompt")
_start_turn(
active_client,
state,
(
str(recovery_prompt)
if resuming_thread
and isinstance(recovery_prompt, str)
and recovery_prompt.strip()
else "Continue the original delegated task from the persisted app-server "
"thread. Use retained evidence and satisfy the original result contract."
if resuming_thread
else prompt
),
[] if resuming_thread else list(metadata.get("attachments", [])),
state_lock=state_lock,
)
update(directory, recovery_prompt=None)
recovery_control_applied = _settle_recovery_control(
directory,
state,
status="applied",
)
if recovery_control_applied:
append_audit(
str(metadata["session_id"]),
"agent_controlled",
caller_agent=state.get("recovery_controller_agent"),
caller_job_id=state.get("recovery_controller_job_id"),
target_job_id=metadata["job_id"],
action=state.get("recovery_action"),
control_revision=state.get("recovery_control_revision"),
delivery="durable_relaunch_ready",
)
(
result_text,
exit_code,
contract_repair_deadline,
finalization_expired,
suspension_exit,
) = _run_worker_event_loop(
runner=runner,
directory=directory,
metadata=metadata,
session=session,
state=state,
state_lock=state_lock,
host=host,
connect_app_server=connect_app_server,
resume_recovered_work=resume_recovered_work,
latest_history_result=latest_history_result,
execution_mode=execution_mode,
terminal_output_schema=terminal_output_schema,
lifecycle_timeout=lifecycle_timeout,
control_failed=control_failed,
control_errors=control_errors,
control_stop=control_stop,
progress=progress,
cwd=cwd,
events_path=events_path,
initial_result_text=result_text,
)
if suspension_exit is not None:
return suspension_exit
if runner.stop_requested:
raise KeyboardInterrupt
if not result_text and not finalization_expired:
result_text = latest_history_result()
result_path.write_text(result_text, encoding="utf-8")
os.chmod(result_path, 0o600)
if output_contract is not None and metadata.get("contract_enforcement") == "strict":
with state_lock:
state["output_schema"] = terminal_output_schema
result_text = _repair_strict_contract(
runner=runner,
directory=directory,
state=state,
state_lock=state_lock,
metadata=metadata,
output_contract=output_contract,
result_text=result_text,
result_path=result_path,
deadline=contract_repair_deadline,
)
# Contract repair is itself a persisted same-thread turn. Capture
# terminal history only after that optional turn so the immutable
# history artifact agrees with the result MMO actually validates and
# publishes. Expired finalization still relies on the lossless JSONL
# event trace and bounded partial evidence rather than extending the
# operator's deadline with another lifecycle request.
terminal_turns = (
[] if finalization_expired else host.complete_history(timeout=lifecycle_timeout)
)
atomic_write_json(
directory / "terminal-history.json",
{
"thread_id": host.thread_id,
"captured_at": utc_now(),
"turns": terminal_turns,
},
)
except FileNotFoundError as exc:
control_stop.set()
failure_exit = _file_not_found_exit(runner, state, codex_bin, exc)
runner.app_server = None
return failure_exit
except AppServerError as exc:
control_stop.set()
failure_exit = _app_server_error_exit(runner, state, started, exc)
runner.app_server = None
return failure_exit
except KeyboardInterrupt:
control_stop.set()
failure_exit = _cancelled_worker_exit(runner, started)
runner.app_server = None
return failure_exit
except Exception as exc:
_settle_recovery_control(
directory,
state,
status=("delivery_unknown" if state.get("recovery_control_dispatched") else "failed"),
error=f"{type(exc).__name__}: {exc}",
)
runner.terminate_child()
return _fail(
directory,
1,
f"worker runner failure: {type(exc).__name__}: {exc}",
runner=runner,
)
return _publish_worker_terminal(
runner=runner,
metadata=metadata,
session=session,
state=state,
control_stop=control_stop,
control_thread=control_thread,
started=started,
exit_code=exit_code,
)
def main() -> int:
if len(sys.argv) != 2:
print("usage: worker_runner.py JOB_DIR", file=sys.stderr)
return 2
return WorkerRunner(Path(sys.argv[1]).resolve()).run()
if __name__ == "__main__":
raise SystemExit(main())