"""Opaque OSS exchange handles used between the trusted Agent runtime and ARR.""" from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path from typing import Mapping, Optional from arr_ingestion.contracts import OPAQUE_ID_RE from arr_storage.contracts import BackendObject, valid_object_key from arr_storage.remote import CloudClientError, CloudClientPort def _prefix(value: str) -> str: normalized = value.strip().strip("/") probe = normalized + "/file_handle" if not normalized or not valid_object_key(probe): raise ValueError("Agent output exchange prefix is invalid") return normalized @dataclass(frozen=True) class OutputExchangeConfig: prefix: str = "arr-agent-outputs" def __post_init__(self) -> None: object.__setattr__(self, "prefix", _prefix(self.prefix)) @classmethod def from_environment( cls, environment: Optional[Mapping[str, str]] = None, ) -> "OutputExchangeConfig": values = environment if environment is not None else os.environ return cls(str(values.get("ARR_AGENT_OUTPUT_PREFIX", "arr-agent-outputs"))) def object_key(self, file_handle: str) -> str: if not OPAQUE_ID_RE.fullmatch(file_handle): raise ValueError("Agent output file handle is invalid") return f"{self.prefix}/{file_handle}" class OutputExchangePublisher: """Runtime-side idempotent publisher for one opaque output handle.""" def __init__(self, client: CloudClientPort, config: OutputExchangeConfig) -> None: self._client = client self._config = config def publish( self, *, file_handle: str, source: Path, mime_type: str, metadata: Mapping[str, str], ) -> BackendObject: object_key = self._config.object_key(file_handle) try: return self._client.upload_file_if_absent( object_key, str(source), mime_type, metadata, ) except CloudClientError as error: if error.kind != "conflict": raise existing = self._client.stat_object(object_key) if ( existing.byte_size != int(metadata.get("arr-byte-size", "-1")) or existing.metadata.get("arr-sha256") != metadata.get("arr-sha256") or existing.metadata.get("arr-mime-type") != mime_type ): raise CloudClientError("conflict") from None return existing