88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
"""Bridge contract surface for Task Admission Bootstrap.
|
|
|
|
This module is intentionally transport-agnostic. The live Bridge may merge
|
|
these routes/operations into its existing authenticated/HTTPS surface, but the
|
|
authorization proof for the authorize operation is the verified delegation
|
|
epoch or Owner signature carried by the request itself.
|
|
|
|
No handler here can issue TaskPrincipal assertions or WriterLeases. Those remain
|
|
strictly downstream of a real admitted/claimed Agent Task.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Mapping
|
|
|
|
from aios_task_admission_bootstrap import TaskAdmissionBootstrapService
|
|
|
|
|
|
TASK_ADMISSION_ROUTES = {
|
|
"/v2/task-admission/envelope": "getTaskAdmissionEnvelope",
|
|
"/v2/task-admission/authorize": "submitTaskAdmissionAuthorization",
|
|
"/v2/task-admission/status": "getTaskAdmissionStatus",
|
|
}
|
|
|
|
TASK_ADMISSION_OPERATION_REQUIRED_FIELDS = {
|
|
"getTaskAdmissionEnvelope": (
|
|
"idempotency_key",
|
|
"task_id",
|
|
"repository",
|
|
"authority_base_oid",
|
|
"target_branch",
|
|
"generation",
|
|
"execution_digest",
|
|
"requested_capabilities",
|
|
"file_allowlist",
|
|
"risk",
|
|
),
|
|
"submitTaskAdmissionAuthorization": (
|
|
"envelope_id",
|
|
"authorization_kind",
|
|
),
|
|
"getTaskAdmissionStatus": ("envelope_id",),
|
|
}
|
|
|
|
TASK_ADMISSION_OPERATION_OPTIONAL_FIELDS = {
|
|
"getTaskAdmissionEnvelope": ("expires_in_seconds",),
|
|
"submitTaskAdmissionAuthorization": (
|
|
"delegation_epoch_id",
|
|
"owner_key_id",
|
|
"signature_base64",
|
|
),
|
|
"getTaskAdmissionStatus": (),
|
|
}
|
|
|
|
|
|
class TaskAdmissionBridgeContract:
|
|
def __init__(self, service: TaskAdmissionBootstrapService) -> None:
|
|
self.service = service
|
|
|
|
@staticmethod
|
|
def _validate_fields(operation: str, request: Mapping[str, Any]) -> None:
|
|
required = set(TASK_ADMISSION_OPERATION_REQUIRED_FIELDS[operation])
|
|
allowed = required | set(TASK_ADMISSION_OPERATION_OPTIONAL_FIELDS[operation])
|
|
if not required.issubset(request):
|
|
raise ValueError("TASK_ADMISSION_BRIDGE_REQUIRED_FIELDS_MISSING")
|
|
if set(request) - allowed:
|
|
raise ValueError("TASK_ADMISSION_BRIDGE_UNKNOWN_FIELDS")
|
|
|
|
def dispatch(self, operation: str, request: Mapping[str, Any]) -> dict[str, Any]:
|
|
if operation not in TASK_ADMISSION_OPERATION_REQUIRED_FIELDS:
|
|
raise ValueError("TASK_ADMISSION_BRIDGE_OPERATION_NOT_FOUND")
|
|
self._validate_fields(operation, request)
|
|
if operation == "getTaskAdmissionEnvelope":
|
|
return self.service.create_envelope(request)
|
|
if operation == "submitTaskAdmissionAuthorization":
|
|
return self.service.authorize_and_admit(request)
|
|
if operation == "getTaskAdmissionStatus":
|
|
return self.service.status(str(request["envelope_id"]))
|
|
raise ValueError("TASK_ADMISSION_BRIDGE_OPERATION_NOT_FOUND")
|
|
|
|
def handlers(self) -> dict[str, Any]:
|
|
return {
|
|
operation: (
|
|
lambda request, operation=operation: self.dispatch(operation, request)
|
|
)
|
|
for operation in TASK_ADMISSION_OPERATION_REQUIRED_FIELDS
|
|
}
|