Some checks failed
Governed runtime contract / contract (push) Failing after 23s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
230 lines
8.5 KiB
Python
230 lines
8.5 KiB
Python
"""Profile-authoritative execution of activity-core ops runs through Glas.
|
|
|
|
Glas and sand-boxer are sibling runtime packages rather than hard dependencies
|
|
of rein-aharness's legacy paths. Imports therefore stay at the execution edge:
|
|
profile-absent coexistence continues to work, while a profiled row fails closed
|
|
with an actionable error if the governed runtime is not installed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
from collections.abc import Callable
|
|
from contextlib import nullcontext
|
|
from typing import Any
|
|
|
|
from rein_aharness.execution_cancel import ExecutionCancel, ExecutionCancelled, resolve_cancel
|
|
from rein_aharness.ops_run_client import OpsRun, OpsRunConfig, resolve_ops_target
|
|
from rein_aharness.spend_admission import SpendAdmissionError, worker_spend
|
|
|
|
GLAS_APPROACH = "glas-profile"
|
|
GLAS_ACTOR = "agt"
|
|
|
|
_SCALAR_REFS = (
|
|
"correlation_id",
|
|
"assignment_ref",
|
|
"role_ref",
|
|
"duty_ref",
|
|
)
|
|
_LIST_REFS = ("goal_refs", "resource_envelope_refs")
|
|
_EVIDENCE_STRING_FIELDS = (
|
|
"request_id",
|
|
"correlation_id",
|
|
"actor",
|
|
"project",
|
|
"target_repo",
|
|
"contract_version",
|
|
"profile_ref",
|
|
"rein_id",
|
|
"rein_version",
|
|
"model_route",
|
|
"resolved_model",
|
|
"sandbox_profile",
|
|
"sandbox_id",
|
|
"tool_profile",
|
|
"outcome",
|
|
"failure_stage",
|
|
"error",
|
|
"started_at",
|
|
"finished_at",
|
|
"commit_sha",
|
|
"tool_events_completeness",
|
|
)
|
|
_EVIDENCE_NUMBER_FIELDS = (
|
|
"duration_s",
|
|
"tokens_spent",
|
|
"cost_usd",
|
|
"token_budget",
|
|
"tool_events_count",
|
|
)
|
|
|
|
|
|
class GlasExecutionError(RuntimeError):
|
|
"""The authoritative Glas invocation could not produce a GatewayResult."""
|
|
|
|
|
|
class GlasSpendError(GlasExecutionError):
|
|
"""Spend refusal with bounded, operator-safe reason text."""
|
|
|
|
|
|
def normalise_execution_evidence_for_close(raw: Any) -> dict[str, Any]:
|
|
"""Retain only bounded Glas evidence fields safe for durable close state."""
|
|
if not isinstance(raw, dict):
|
|
return {}
|
|
result: dict[str, Any] = {}
|
|
for key in _EVIDENCE_STRING_FIELDS:
|
|
value = raw.get(key)
|
|
if isinstance(value, str):
|
|
result[key] = value[:2000] if key == "error" else value[:500]
|
|
for key in _EVIDENCE_NUMBER_FIELDS:
|
|
value = raw.get(key)
|
|
if (
|
|
isinstance(value, (int, float))
|
|
and not isinstance(value, bool)
|
|
and math.isfinite(value)
|
|
and value >= 0
|
|
):
|
|
result[key] = value
|
|
artifacts = raw.get("artifacts")
|
|
if isinstance(artifacts, list):
|
|
result["artifacts"] = [
|
|
item[:1000]
|
|
for item in artifacts[:20]
|
|
if isinstance(item, str) and item
|
|
]
|
|
refs: dict[str, Any] = {}
|
|
raw_refs = raw.get("refs")
|
|
if isinstance(raw_refs, dict):
|
|
for key in _SCALAR_REFS:
|
|
value = raw_refs.get(key)
|
|
if isinstance(value, str) and value:
|
|
refs[key] = value[:500]
|
|
for key in _LIST_REFS:
|
|
value = raw_refs.get(key)
|
|
if isinstance(value, list):
|
|
refs[key] = [
|
|
item[:500]
|
|
for item in value[:50]
|
|
if isinstance(item, str) and item
|
|
]
|
|
if refs:
|
|
result["refs"] = refs
|
|
return result
|
|
|
|
|
|
def _request_kwargs(run: OpsRun, config: OpsRunConfig, report_to_hub: bool) -> dict[str, Any]:
|
|
if not run.harness_profile_ref:
|
|
raise GlasExecutionError(f"ops_run {run.id} has no harness_profile_ref")
|
|
|
|
refs = run.execution_refs
|
|
kwargs: dict[str, Any] = {
|
|
"harness_profile_ref": run.harness_profile_ref,
|
|
"repo": str(resolve_ops_target(run, config)),
|
|
"title": run.title or "(untitled)",
|
|
"description": run.description or "",
|
|
# Activity Core's worker_id owns the queue lease; it is not a Glas
|
|
# actor type. Sand-boxer validates governed execution actors as
|
|
# adm|agt|atm, so this runtime enters the gateway as an agent.
|
|
"actor": GLAS_ACTOR,
|
|
"project": config.execution_project,
|
|
"request_id": run.id,
|
|
"report_to_hub": report_to_hub,
|
|
}
|
|
for key in _SCALAR_REFS:
|
|
value = refs.get(key)
|
|
if isinstance(value, str) and value:
|
|
kwargs[key] = value
|
|
for key in _LIST_REFS:
|
|
value = refs.get(key)
|
|
if isinstance(value, list):
|
|
kwargs[key] = [item for item in value if isinstance(item, str)]
|
|
return kwargs
|
|
|
|
|
|
def execute_profiled_run(
|
|
run: OpsRun,
|
|
config: OpsRunConfig,
|
|
*,
|
|
report_to_hub: bool = True,
|
|
request_factory: Callable[..., Any] | None = None,
|
|
gateway: Callable[[Any], Any] | None = None,
|
|
cancel: ExecutionCancel | None = None,
|
|
transaction: Any = None,
|
|
) -> dict[str, Any]:
|
|
"""Invoke Glas and return its complete JSON-compatible GatewayResult."""
|
|
guard = resolve_cancel(cancel)
|
|
if guard is not None:
|
|
guard.check()
|
|
if request_factory is None or gateway is None:
|
|
try:
|
|
from glas_harness.contract import ExecutionRequest
|
|
from glas_harness.gateway import run_execution
|
|
except ImportError as exc:
|
|
raise GlasExecutionError(
|
|
"profiled ops_run requires glas-harness with sand-boxer support; "
|
|
"install the sibling checkouts into the claim-worker environment"
|
|
) from exc
|
|
request_factory = request_factory or ExecutionRequest
|
|
gateway = gateway or run_execution
|
|
|
|
transfer = None
|
|
if run.repository_grant is not None:
|
|
from rein_aharness.repository_artifact import RepositoryArtifactTransfer
|
|
transfer = RepositoryArtifactTransfer(transaction, run.repository_grant, cancel=guard)
|
|
|
|
try:
|
|
request = request_factory(**_request_kwargs(run, config, report_to_hub))
|
|
kwargs = {"artifact_capture": transfer.capture} if transfer else {}
|
|
spend = worker_spend(config)
|
|
owner = config.messages_owner
|
|
if config.require_request_admission and owner is None:
|
|
raise SpendAdmissionError("required request owner is not configured")
|
|
if owner is not None and (spend is None or guard is None):
|
|
raise SpendAdmissionError("request owner requires spend and cancellation")
|
|
if spend is not None:
|
|
from glas_harness.profiles import ProfileCatalog
|
|
catalog = ProfileCatalog()
|
|
profile, descriptor = catalog.resolve(run.harness_profile_ref)
|
|
catalog.require_operational(profile)
|
|
spend.validate_dispatch(run, config, request, profile, descriptor)
|
|
# The same cached catalog supplies the checked profile to Glas.
|
|
kwargs["catalog"] = catalog
|
|
if guard is not None:
|
|
guard.check()
|
|
spend.reserve(run)
|
|
route = owner.activate(run, config, spend, profile, guard) if owner else nullcontext(None)
|
|
with route as owner_manager:
|
|
if owner_manager is not None:
|
|
kwargs["manager"] = owner_manager
|
|
result = gateway(request, **kwargs)
|
|
raw = result.model_dump(mode="json") if hasattr(result, "model_dump") else result
|
|
except ExecutionCancelled:
|
|
raise
|
|
except SpendAdmissionError as exc:
|
|
raise GlasSpendError(f"spend admission refused: {exc}") from None
|
|
except GlasExecutionError:
|
|
raise
|
|
except Exception as exc:
|
|
if guard is not None and guard.cancelled:
|
|
raise ExecutionCancelled(guard.reason or "cancelled") from None
|
|
raise GlasExecutionError(f"Glas gateway invocation failed: {exc}") from exc
|
|
if guard is not None:
|
|
guard.check()
|
|
|
|
if not isinstance(raw, dict) or not isinstance(raw.get("ok"), bool):
|
|
raise GlasExecutionError("Glas gateway returned an invalid GatewayResult")
|
|
if not isinstance(raw.get("evidence"), dict):
|
|
raise GlasExecutionError("Glas GatewayResult is missing execution evidence")
|
|
if spend is not None:
|
|
try:
|
|
if not spend.observe(run.id, raw):
|
|
raise SpendAdmissionError("execution accounting requires reconciliation")
|
|
except SpendAdmissionError as exc:
|
|
raise GlasSpendError(f"spend accounting refused: {exc}") from None
|
|
if transfer is not None and raw["ok"]:
|
|
evidence = raw["evidence"]
|
|
if evidence.get("session_cleanup") != "succeeded" or evidence.get("sandbox_destroy") != "succeeded":
|
|
raise GlasExecutionError("artifact import requires confirmed session cleanup and sandbox teardown")
|
|
imported = transfer.import_after_teardown(evidence.get("commit_sha"))
|
|
raw["artifact_import"] = imported
|
|
return raw
|