Files
aios/active-runtime/app/aios_agent_task_api_lineage_supersession.py
T

123 lines
4.4 KiB
Python

"""Staging HTTP overlay for the exact Task lineage supersession capability."""
from __future__ import annotations
from http.server import ThreadingHTTPServer
from typing import Any, Callable, Mapping
from urllib.parse import urlparse
from aios_task_lineage_supersession import (
DECISION_SCHEMA,
TaskLineageSupersessionController,
TaskLineageSupersessionError,
)
SUPERSESSION_ROUTE = "/v1/control/task-lineage-supersession"
def _status(error: TaskLineageSupersessionError) -> int:
return error.http_status
class LineageSupersessionEndpoint:
"""Framework-neutral endpoint; authentication must precede all parsing/mutation."""
def __init__(
self,
db_path,
verifier,
authenticate_control: Callable[[Mapping[str, str]], Any],
*,
clock=None,
identity_digest=None,
) -> None:
self.authenticate_control = authenticate_control
self.controller = TaskLineageSupersessionController(
db_path, verifier, clock=clock, identity_digest=identity_digest
)
def post(self, headers: Mapping[str, str], body: Mapping[str, Any]) -> tuple[int, dict[str, Any]]:
self.authenticate_control(headers)
try:
result = self.controller.supersede(body)
except TaskLineageSupersessionError as exc:
return _status(exc), {"error": exc.code}
return (200 if result.get("idempotent") else 201), result
def build_runtime():
"""Bind to the already-active recovery API and its formal Owner verifier lazily."""
try:
from aios_agent_task_api import APIConfig
from aios_agent_task_api_p0_failed_lease_recovery import (
P0FailedLeaseRecoveryHandler,
P0FailedLeaseRecoveryTaskApplication,
)
from aios_agent_task_protocol import TaskProtocolError
from aios_owner_approval_verifier import (
GrantRootOwnerApprovalVerifier,
OwnerApprovalVerifierError,
)
except ImportError as exc:
raise RuntimeError("LINEAGE_SUPERSESSION_RUNTIME_OVERLAY_UNAVAILABLE") from exc
# The active entrypoint layers failed- and active-lease recovery on top of
# expired recovery. Inherit that current layer so adding lineage does not
# silently remove either already-deployed recovery route.
class Application(P0FailedLeaseRecoveryTaskApplication):
def __init__(self, config: Any) -> None:
super().__init__(config)
try:
verifier = GrantRootOwnerApprovalVerifier()
except OwnerApprovalVerifierError as exc:
raise TaskProtocolError(str(exc), 503) from exc
self.lineage_supersession = LineageSupersessionEndpoint(
config.db_path, verifier, self.authenticate_control
)
def health(self) -> dict[str, Any]:
value = dict(super().health())
value.update({
"task_lineage_supersession": True,
"task_lineage_supersession_schema": DECISION_SCHEMA,
"task_lineage_supersession_exact_scope": True,
"task_lineage_supersession_exact_cas": True,
"task_lineage_supersession_append_only": True,
"task_lineage_supersession_caller_payload": False,
})
return value
class Handler(P0FailedLeaseRecoveryHandler):
app: Application | None = None
def do_POST(self) -> None:
path = urlparse(self.path).path.rstrip("/") or "/"
if path != SUPERSESSION_ROUTE:
super().do_POST()
return
def action() -> None:
if self.app is None:
raise TaskProtocolError("SERVICE_UNAVAILABLE", 503)
status, result = self.app.lineage_supersession.post(
self._headers(), self._body()
)
if "error" in result:
raise TaskProtocolError(str(result["error"]), status)
self._json(result, status)
self._run(action)
return APIConfig, Application, Handler
def serve(config=None) -> None:
APIConfig, Application, Handler = build_runtime()
config = config or APIConfig.from_env()
application = Application(config)
Handler.app = application
ThreadingHTTPServer((config.host, config.port), Handler).serve_forever()
if __name__ == "__main__":
serve()