Compare commits

..
24 changed files with 6055 additions and 719 deletions
+315
View File
@@ -0,0 +1,315 @@
#!/usr/bin/env python3
"""HTTP API for AIOS external-agent online task distribution.
The service is intended to bind to loopback and be published through the
existing TLS reverse proxy. Control operations use a server-managed control
Bearer token. Agents receive individual Bearer tokens at registration; only
token hashes are persisted.
"""
from __future__ import annotations
from dataclasses import dataclass
import hmac
import json
import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import re
from typing import Any, Mapping
from urllib.parse import urlparse
from aios_agent_task_protocol import TaskProtocolError, TaskStore
MAX_BODY = 1024 * 1024
TASK_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
@dataclass(frozen=True, slots=True)
class APIConfig:
db_path: Path
control_token_path: Path
host: str = "127.0.0.1"
port: int = 18940
token_pepper: str = ""
@classmethod
def from_env(cls) -> "APIConfig":
return cls(
db_path=Path(os.getenv(
"AIOS_AGENT_TASK_DB",
"/var/lib/aios-agent-task/state.sqlite3",
)),
control_token_path=Path(os.getenv(
"AIOS_AGENT_TASK_CONTROL_TOKEN_FILE",
"/run/credentials/aios-agent-task-api.service/control_token",
)),
host=os.getenv("AIOS_AGENT_TASK_HOST", "127.0.0.1"),
port=int(os.getenv("AIOS_AGENT_TASK_PORT", "18940")),
token_pepper=os.getenv("AIOS_AGENT_TASK_TOKEN_PEPPER", ""),
)
class AgentTaskApplication:
def __init__(self, config: APIConfig) -> None:
self.config = config
self.store = TaskStore(
config.db_path,
token_pepper=config.token_pepper,
)
def _control_token(self) -> str:
path = self.config.control_token_path
if path.is_symlink() or not path.is_file():
raise TaskProtocolError("CONTROL_CREDENTIAL_UNAVAILABLE", 503)
value = path.read_text(encoding="utf-8").strip()
if len(value) < 32:
raise TaskProtocolError("CONTROL_CREDENTIAL_INVALID", 503)
return value
@staticmethod
def _bearer(headers: Mapping[str, str]) -> str:
value = str(headers.get("Authorization") or "")
if not value.startswith("Bearer "):
return ""
return value[7:].strip()
def authenticate_control(self, headers: Mapping[str, str]) -> None:
supplied = self._bearer(headers)
expected = self._control_token()
if not supplied or not hmac.compare_digest(supplied, expected):
raise TaskProtocolError("UNAUTHORIZED", 401)
def authenticate_agent(self, headers: Mapping[str, str]) -> dict[str, Any]:
return self.store.authenticate_agent(self._bearer(headers))
def principal(self, headers: Mapping[str, str]) -> tuple[str, dict[str, Any] | None]:
supplied = self._bearer(headers)
if not supplied:
raise TaskProtocolError("UNAUTHORIZED", 401)
try:
expected = self._control_token()
except TaskProtocolError:
expected = ""
if expected and hmac.compare_digest(supplied, expected):
return "control", None
return "agent", self.store.authenticate_agent(supplied)
def health(self) -> dict[str, Any]:
return {
"status": "ok",
"schema": "aios.agent-task.v1",
"task_states": [
"QUEUED", "CLAIMED", "RUNNING", "VERIFYING",
"SUCCEEDED", "FAILED", "EXPIRED", "CANCELLED",
],
"lease_recovery": True,
"atomic_claim": True,
"idempotent_result_submission": True,
"server_side_verification_required": True,
"plaintext_credentials_persisted": False,
}
def register_agent(self, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
self.authenticate_control(headers)
return self.store.register_agent(
agent_id=str(body.get("agent_id") or ""),
agent_type=str(body.get("agent_type") or ""),
agent_version=str(body.get("agent_version") or ""),
capabilities=body.get("capabilities") or [],
metadata=body.get("metadata") if isinstance(body.get("metadata"), Mapping) else {},
)
def create_task(self, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
self.authenticate_control(headers)
return self.store.create_task(
task_id=str(body.get("task_id") or "") or None,
goal=str(body.get("goal") or ""),
instructions=str(body.get("instructions") or ""),
success_criteria=body.get("success_criteria"),
required_capabilities=body.get("required_capabilities") or [],
constraints=body.get("constraints") if isinstance(body.get("constraints"), Mapping) else {},
metadata=body.get("metadata") if isinstance(body.get("metadata"), Mapping) else {},
deadline=str(body.get("deadline") or "") or None,
priority=body.get("priority", 0),
max_attempts=body.get("max_attempts", 3),
idempotency_key=str(body.get("idempotency_key") or ""),
)
def poll(self, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
agent = self.authenticate_agent(headers)
tasks = self.store.poll_tasks(
agent_id=agent["agent_id"],
limit=int(body.get("limit", 20)),
)
return {"agent_id": agent["agent_id"], "tasks": tasks}
def claim(self, task_id: str, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
agent = self.authenticate_agent(headers)
return self.store.claim_task(
task_id=task_id,
agent_id=agent["agent_id"],
lease_seconds=int(body.get("lease_seconds", 300)),
)
def heartbeat(self, task_id: str, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
agent = self.authenticate_agent(headers)
return self.store.heartbeat(
task_id=task_id,
agent_id=agent["agent_id"],
lease_token=str(body.get("lease_token") or ""),
progress=body.get("progress") if isinstance(body.get("progress"), Mapping) else {},
lease_seconds=int(body.get("lease_seconds", 300)),
)
def result(self, task_id: str, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
agent = self.authenticate_agent(headers)
result = body.get("result")
if not isinstance(result, Mapping):
raise TaskProtocolError("RESULT_INVALID", 400)
return self.store.submit_result(
task_id=task_id,
agent_id=agent["agent_id"],
lease_token=str(body.get("lease_token") or ""),
idempotency_key=str(body.get("idempotency_key") or ""),
result=result,
)
def verify(self, task_id: str, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
self.authenticate_control(headers)
return self.store.verify_task(
task_id=task_id,
decision=str(body.get("decision") or ""),
verification=body.get("verification") if isinstance(body.get("verification"), Mapping) else {},
)
def cancel(self, task_id: str, headers: Mapping[str, str], body: Mapping[str, Any]) -> dict[str, Any]:
self.authenticate_control(headers)
return self.store.cancel_task(task_id=task_id, reason=str(body.get("reason") or ""))
def get_task(self, task_id: str, headers: Mapping[str, str]) -> dict[str, Any]:
kind, agent = self.principal(headers)
task = self.store.get_task(task_id)
if kind == "agent" and task.get("claimed_by") != agent["agent_id"]:
raise TaskProtocolError("FORBIDDEN", 403)
return task
def events(self, task_id: str, headers: Mapping[str, str]) -> dict[str, Any]:
self.authenticate_control(headers)
return {"task_id": task_id, "events": self.store.get_events(task_id)}
class Handler(BaseHTTPRequestHandler):
app: AgentTaskApplication | None = None
server_version = "AIOSAgentTaskAPI/1"
def log_message(self, _format: str, *_args: Any) -> None:
return
def _headers(self) -> dict[str, str]:
return {key: value for key, value in self.headers.items()}
def _json(self, value: Any, status: int = 200) -> None:
payload = json.dumps(value, ensure_ascii=False, sort_keys=True).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def _body(self) -> dict[str, Any]:
try:
size = int(self.headers.get("Content-Length", "0"))
except ValueError as exc:
raise TaskProtocolError("CONTENT_LENGTH_INVALID", 400) from exc
if size < 0 or size > MAX_BODY:
raise TaskProtocolError("REQUEST_TOO_LARGE", 413)
raw = self.rfile.read(size) if size else b"{}"
try:
value = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise TaskProtocolError("JSON_INVALID", 400) from exc
if not isinstance(value, dict):
raise TaskProtocolError("JSON_OBJECT_REQUIRED", 400)
return value
@staticmethod
def _task_route(path: str) -> tuple[str, str | None]:
match = re.fullmatch(
r"/v1/tasks/([A-Za-z0-9][A-Za-z0-9._:-]{2,179})(?:/(claim|heartbeat|result|verify|cancel|events))?",
path,
)
if match:
return match.group(1), match.group(2)
return "", None
def _run(self, callback) -> None:
try:
callback()
except TaskProtocolError as exc:
self._json({"error": exc.code}, exc.http_status)
except Exception as exc:
self._json({"error": "INTERNAL_ERROR", "detail": type(exc).__name__}, 500)
def do_GET(self) -> None:
def action() -> None:
if self.app is None:
raise TaskProtocolError("SERVICE_UNAVAILABLE", 503)
path = urlparse(self.path).path.rstrip("/") or "/"
if path == "/health":
self._json(self.app.health())
return
task_id, suffix = self._task_route(path)
if task_id and suffix is None:
self._json(self.app.get_task(task_id, self._headers()))
return
if task_id and suffix == "events":
self._json(self.app.events(task_id, self._headers()))
return
self._json({"error": "NOT_FOUND"}, 404)
self._run(action)
def do_POST(self) -> None:
def action() -> None:
if self.app is None:
raise TaskProtocolError("SERVICE_UNAVAILABLE", 503)
path = urlparse(self.path).path.rstrip("/") or "/"
body = self._body()
if path == "/v1/agents/register":
self._json(self.app.register_agent(self._headers(), body), 201)
return
if path == "/v1/tasks":
self._json(self.app.create_task(self._headers(), body), 201)
return
if path == "/v1/tasks/poll":
self._json(self.app.poll(self._headers(), body))
return
task_id, suffix = self._task_route(path)
if not task_id:
self._json({"error": "NOT_FOUND"}, 404)
return
if suffix == "claim":
self._json(self.app.claim(task_id, self._headers(), body))
elif suffix == "heartbeat":
self._json(self.app.heartbeat(task_id, self._headers(), body))
elif suffix == "result":
self._json(self.app.result(task_id, self._headers(), body))
elif suffix == "verify":
self._json(self.app.verify(task_id, self._headers(), body))
elif suffix == "cancel":
self._json(self.app.cancel(task_id, self._headers(), body))
else:
self._json({"error": "NOT_FOUND"}, 404)
self._run(action)
def serve(config: APIConfig | None = None) -> None:
config = config or APIConfig.from_env()
application = AgentTaskApplication(config)
Handler.app = application
ThreadingHTTPServer((config.host, config.port), Handler).serve_forever()
if __name__ == "__main__":
serve()
@@ -192,6 +192,140 @@ class InitialExecutionProjectionRequest:
}
@dataclass(frozen=True)
class SourceCaptureProjectionRecoveryRequest:
"""CAS contract for the first durable runtime projection after source capture.
Some deployed Bridge versions materialize a binding by letting the generic
worker perform one read-only ``source_capture`` attempt. This request does
not relax the pristine initial-projection contract above. It authorizes a
separate, evidence-bound transition only after that attempt has been
officially verified as ``NEEDS_REWORK`` and returned to ``QUEUED``.
"""
expected_status: str
expected_revision: int
expected_attempt: int
expected_metadata_sha256: str
expected_required_capabilities_sha256: str
previous_verification_sha256: str
expected_source_capture_result_sha256: str
expected_source_capture_execution_digest: str
expected_job_id: str
expected_execution_digest: str
required_capability: str
adapter: str
authorization_id: str
idempotency_key: str
reason: str
@classmethod
def from_mapping(
cls, value: Mapping[str, Any]
) -> "SourceCaptureProjectionRecoveryRequest":
allowed = {
"expected_status",
"expected_revision",
"expected_attempt",
"expected_metadata_sha256",
"expected_required_capabilities_sha256",
"previous_verification_sha256",
"expected_source_capture_result_sha256",
"expected_source_capture_execution_digest",
"expected_job_id",
"expected_execution_digest",
"required_capability",
"adapter",
"authorization_id",
"idempotency_key",
"reason",
}
if set(value) != allowed:
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_PROJECTION_RECOVERY_FIELDS_INVALID", 400
)
status = _require_string(value["expected_status"], "EXPECTED_STATUS_INVALID")
revision = value["expected_revision"]
attempt = value["expected_attempt"]
if (
status != "QUEUED"
or not isinstance(revision, int)
or isinstance(revision, bool)
or revision < 2
or not isinstance(attempt, int)
or isinstance(attempt, bool)
or attempt < 1
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_PROJECTION_RECOVERY_STATE_INVALID", 400
)
digest_fields = {
"expected_metadata_sha256": "METADATA_SHA256_INVALID",
"expected_required_capabilities_sha256": "CAPABILITIES_SHA256_INVALID",
"previous_verification_sha256": "VERIFICATION_SHA256_INVALID",
"expected_source_capture_result_sha256": "SOURCE_CAPTURE_RESULT_SHA256_INVALID",
"expected_source_capture_execution_digest": "SOURCE_CAPTURE_EXECUTION_DIGEST_INVALID",
"expected_execution_digest": "EXECUTION_DIGEST_INVALID",
}
digests: dict[str, str] = {}
for name, code in digest_fields.items():
digests[name] = _require_string(value[name], code, SHA_RE)
job_id = _require_string(value["expected_job_id"], "JOB_ID_INVALID", JOB_RE)
capability = _require_string(
value["required_capability"],
"RUNTIME_CAPABILITY_INVALID",
RUNTIME_CAPABILITY_RE,
)
adapter = _require_string(value["adapter"], "RUNTIME_ADAPTER_INVALID")
if capability != f"agent.runtime.{adapter}" or adapter not in {"hermes", "openclaw"}:
raise InitialExecutionProjectionError("RUNTIME_CAPABILITY_MISMATCH", 400)
authorization_id = _require_string(
value["authorization_id"], "AUTHORIZATION_ID_INVALID", KEY_RE
)
idempotency_key = _require_string(
value["idempotency_key"], "IDEMPOTENCY_KEY_INVALID", KEY_RE
)
reason = _require_string(value["reason"], "PROJECTION_REASON_INVALID")
if len(reason) > 512:
raise InitialExecutionProjectionError("PROJECTION_REASON_INVALID", 400)
return cls(
status,
revision,
attempt,
digests["expected_metadata_sha256"],
digests["expected_required_capabilities_sha256"],
digests["previous_verification_sha256"],
digests["expected_source_capture_result_sha256"],
digests["expected_source_capture_execution_digest"],
job_id,
digests["expected_execution_digest"],
capability,
adapter,
authorization_id,
idempotency_key,
reason,
)
def public(self) -> dict[str, Any]:
return {
"expected_status": self.expected_status,
"expected_revision": self.expected_revision,
"expected_attempt": self.expected_attempt,
"expected_metadata_sha256": self.expected_metadata_sha256,
"expected_required_capabilities_sha256": self.expected_required_capabilities_sha256,
"previous_verification_sha256": self.previous_verification_sha256,
"expected_source_capture_result_sha256": self.expected_source_capture_result_sha256,
"expected_source_capture_execution_digest": self.expected_source_capture_execution_digest,
"expected_job_id": self.expected_job_id,
"expected_execution_digest": self.expected_execution_digest,
"required_capability": self.required_capability,
"adapter": self.adapter,
"authorization_id": self.authorization_id,
"idempotency_key": self.idempotency_key,
"reason": self.reason,
}
class CanonicalBridgeInitialExecutionResolver:
"""Resolve one agent-runtime envelope from the canonical governed Bridge."""
@@ -377,6 +511,191 @@ class InitialExecutionProjectionStore:
"""
)
@staticmethod
def _ensure_source_capture_recovery_table(
connection: sqlite3.Connection,
) -> None:
connection.execute(
"""
CREATE TABLE IF NOT EXISTS task_source_capture_projection_recoveries (
task_id TEXT NOT NULL,
idempotency_key TEXT NOT NULL,
request_sha256 TEXT NOT NULL,
authorization_id TEXT NOT NULL,
expected_job_id TEXT NOT NULL,
source_capture_result_sha256 TEXT NOT NULL,
source_capture_execution_digest TEXT NOT NULL,
execution_digest TEXT NOT NULL,
required_capability TEXT NOT NULL,
adapter TEXT NOT NULL,
expected_revision INTEGER NOT NULL,
applied_revision INTEGER NOT NULL,
attempt INTEGER NOT NULL,
created_at TEXT NOT NULL,
PRIMARY KEY(task_id,idempotency_key)
)
"""
)
@classmethod
def _source_capture_event_chain(
cls, connection: sqlite3.Connection, task_id: str
) -> None:
table, columns = cls._event_table(connection)
ordering = "event_id" if "event_id" in columns else "rowid"
rows = connection.execute(
f'SELECT event_type, payload_json FROM "{table}" '
f'WHERE task_id=? ORDER BY {ordering}',
(task_id,),
).fetchall()
events = [str(row[0]) for row in rows]
if (
len(events) < 5
or events[0] != "TASK_CREATED"
or events.count("TASK_CLAIMED") != 1
or events.count("RESULT_SUBMITTED") != 1
or events.count("TASK_VERIFIED") != 1
or events[-1] != "TASK_VERIFIED"
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RECOVERY_EVENT_CHAIN_INVALID", 409
)
claim = events.index("TASK_CLAIMED")
result = events.index("RESULT_SUBMITTED")
verified = events.index("TASK_VERIFIED")
heartbeat_positions = [
index for index, event in enumerate(events) if event == "TASK_HEARTBEAT"
]
if (
not heartbeat_positions
or not all(claim < index < result for index in heartbeat_positions)
or not claim < result < verified
or any(
event not in {
"TASK_CREATED",
"TASK_CLAIMED",
"TASK_HEARTBEAT",
"RESULT_SUBMITTED",
"TASK_VERIFIED",
}
for event in events
)
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RECOVERY_EVENT_CHAIN_INVALID", 409
)
try:
verification_event = json.loads(rows[-1][1])
except (TypeError, json.JSONDecodeError) as exc:
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RECOVERY_VERIFICATION_EVENT_INVALID", 409
) from exc
if (
not isinstance(verification_event, Mapping)
or verification_event.get("decision") != "NEEDS_REWORK"
or verification_event.get("next_state") != "QUEUED"
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RECOVERY_VERIFICATION_EVENT_INVALID", 409
)
def _load_source_capture_recovery(
self,
connection: sqlite3.Connection,
task_id: str,
request: SourceCaptureProjectionRecoveryRequest,
) -> tuple[sqlite3.Row, dict[str, Any], dict[str, Any]]:
columns = self._columns(connection, "tasks")
required = {
"task_id", "goal", "instructions", "success_criteria_json",
"constraints_json", "required_capabilities_json", "metadata_json",
"status", "attempt", "revision", "claimed_by", "lease_token_hash",
"lease_expires_at", "result_json", "verification_json",
}
if not required.issubset(columns):
raise InitialExecutionProjectionError(
"TASK_SCHEMA_SOURCE_CAPTURE_RECOVERY_UNSUPPORTED", 503
)
task = connection.execute(
"SELECT * FROM tasks WHERE task_id=?", (task_id,)
).fetchone()
if task is None:
raise InitialExecutionProjectionError("TASK_NOT_FOUND", 404)
if (
str(task["status"]) != request.expected_status
or int(task["revision"]) != request.expected_revision
or int(task["attempt"]) != request.expected_attempt
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_PROJECTION_RECOVERY_CAS_MISMATCH", 409
)
if task["claimed_by"] not in (None, ""):
raise InitialExecutionProjectionError("TASK_ALREADY_CLAIMED", 409)
if task["lease_token_hash"] not in (None, "") or task["lease_expires_at"] not in (None, ""):
raise InitialExecutionProjectionError("ACTIVE_LEASE_DENY", 409)
metadata = self._json_value(task["metadata_json"], "TASK_METADATA_INVALID")
capabilities = self._json_value(
task["required_capabilities_json"], "TASK_CAPABILITIES_INVALID"
)
result = self._json_value(
task["result_json"], "SOURCE_CAPTURE_RESULT_INVALID"
)
verification = self._json_value(
task["verification_json"], "SOURCE_CAPTURE_VERIFICATION_INVALID"
)
if (
not isinstance(metadata, Mapping)
or not isinstance(capabilities, list)
or not isinstance(result, Mapping)
or not isinstance(verification, Mapping)
):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RECOVERY_STATE_INVALID", 409
)
metadata = dict(metadata)
result = dict(result)
verification = dict(verification)
if "execution" in metadata or "execution_projection" in metadata:
raise InitialExecutionProjectionError("TASK_EXECUTION_ALREADY_PROJECTED", 409)
if capabilities:
raise InitialExecutionProjectionError(
"TASK_INITIAL_CAPABILITIES_MUST_BE_EMPTY", 409
)
if _sha(metadata) != request.expected_metadata_sha256:
raise InitialExecutionProjectionError("TASK_METADATA_DRIFT", 409)
if _sha(capabilities) != request.expected_required_capabilities_sha256:
raise InitialExecutionProjectionError("TASK_CAPABILITIES_DRIFT", 409)
if _sha(result) != request.expected_source_capture_result_sha256:
raise InitialExecutionProjectionError("SOURCE_CAPTURE_RESULT_DRIFT", 409)
if _sha(verification) != request.previous_verification_sha256:
raise InitialExecutionProjectionError("SOURCE_CAPTURE_VERIFICATION_DRIFT", 409)
if verification.get("decision") != "NEEDS_REWORK":
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_NEEDS_REWORK_VERIFICATION_REQUIRED", 409
)
expected_result = {
"task_id": task_id,
"job_id": request.expected_job_id,
"status": "SUCCEEDED",
"classification": "NEEDS_REWORK_SOURCE_CAPTURE",
"execution_mode": "source_capture",
"execution_digest": request.expected_source_capture_execution_digest,
"changed_files": [],
"writer_lease": "NONE",
"verification_state": "NEEDS_REWORK",
"direct_main_write": False,
"production_write": False,
"force_push": False,
"protected_ref_bypass": False,
"secret_material_exposed": False,
}
if any(result.get(name) != expected for name, expected in expected_result.items()):
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_RESULT_CONTRACT_INVALID", 409
)
self._source_capture_event_chain(connection, task_id)
return task, metadata, result
def _load_and_validate(
self,
connection: sqlite3.Connection,
@@ -632,6 +951,222 @@ class InitialExecutionProjectionStore:
"idempotent": False,
}
def project_from_source_capture(
self,
task_id: str,
request: SourceCaptureProjectionRecoveryRequest,
) -> dict[str, Any]:
task_id = _require_string(task_id, "TASK_ID_INVALID", TASK_RE)
if request.authorization_id != _required_authorization_id():
raise InitialExecutionProjectionError("AUTHORIZATION_ID_MISMATCH", 403)
request_sha = _sha(request.public())
def replay_result(row: sqlite3.Row) -> dict[str, Any]:
return {
"task_id": task_id,
"status": "QUEUED",
"attempt": int(row["attempt"]),
"revision": int(row["applied_revision"]),
"job_id": row["expected_job_id"],
"source_capture_execution_digest": row[
"source_capture_execution_digest"
],
"execution_digest": row["execution_digest"],
"required_capability": row["required_capability"],
"adapter": row["adapter"],
"authorization_id": row["authorization_id"],
"recovery_from_source_capture": True,
"idempotent": True,
}
with self._connect() as connection:
self._ensure_source_capture_recovery_table(connection)
replay = connection.execute(
"SELECT * FROM task_source_capture_projection_recoveries "
"WHERE task_id=? AND idempotency_key=?",
(task_id, request.idempotency_key),
).fetchone()
if replay is not None:
if replay["request_sha256"] != request_sha:
raise InitialExecutionProjectionError("IDEMPOTENCY_CONFLICT", 409)
connection.commit()
return replay_result(replay)
with self._connect() as connection:
task, _, result = self._load_source_capture_recovery(
connection, task_id, request
)
runtime_input = self._runtime_input(task)
resolved = dict(
self.resolver(
task_id,
request.expected_job_id,
request.required_capability,
request.adapter,
runtime_input,
)
)
binding = GovernedExecutionBinding.from_mapping(resolved)
if binding.task_id != task_id or binding.job_id != request.expected_job_id:
raise InitialExecutionProjectionError("GOVERNED_BINDING_CONFLICT", 409)
result_binding = {
"task_id": result.get("task_id"),
"job_id": result.get("job_id"),
"repository": result.get("repository"),
"base_oid": result.get("base_oid"),
"expected_branch_oid": result.get("expected_branch_oid"),
"target_branch": result.get("target_branch"),
"generation": result.get("generation"),
}
expected_binding = {
"task_id": binding.task_id,
"job_id": binding.job_id,
"repository": binding.repository,
"base_oid": binding.base_oid,
"expected_branch_oid": binding.expected_branch_oid,
"target_branch": binding.target_branch,
"generation": binding.generation,
}
if result_binding != expected_binding:
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_GOVERNED_BINDING_CONFLICT", 409
)
execution = validate_execution_envelope(resolved, binding=binding)
if (
execution.get("action") != "agent_runtime"
or execution.get("adapter") != request.adapter
or execution.get("capability_scope") != [request.required_capability]
or execution.get("change_set") != []
or execution.get("verification") != []
or execution.get("governance", {}).get("writer_lease") is not False
):
raise InitialExecutionProjectionError("AGENT_RUNTIME_ENVELOPE_INVALID", 409)
if execution["execution_digest"] != request.expected_execution_digest:
raise InitialExecutionProjectionError("EXECUTION_DIGEST_DRIFT", 409)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._ensure_source_capture_recovery_table(connection)
replay = connection.execute(
"SELECT * FROM task_source_capture_projection_recoveries "
"WHERE task_id=? AND idempotency_key=?",
(task_id, request.idempotency_key),
).fetchone()
if replay is not None:
if replay["request_sha256"] != request_sha:
raise InitialExecutionProjectionError("IDEMPOTENCY_CONFLICT", 409)
connection.commit()
return replay_result(replay)
task, metadata, _ = self._load_source_capture_recovery(
connection, task_id, request
)
new_metadata = dict(metadata)
new_metadata["execution"] = execution
new_metadata["execution_projection"] = {
"schema": "aios.online-agent.task-projection.v1",
"task_id": task_id,
"job_id": request.expected_job_id,
"execution_digest": execution["execution_digest"],
}
applied_revision = request.expected_revision + 1
cursor = connection.execute(
"""
UPDATE tasks
SET metadata_json=?, required_capabilities_json=?,
revision=?, updated_at=?
WHERE task_id=? AND status=? AND revision=? AND attempt=?
AND claimed_by IS NULL AND lease_token_hash IS NULL
AND lease_expires_at IS NULL
AND metadata_json=? AND required_capabilities_json=?
AND result_json=? AND verification_json=?
""",
(
json.dumps(new_metadata, sort_keys=True, separators=(",", ":")),
json.dumps([request.required_capability], separators=(",", ":")),
applied_revision,
_now(),
task_id,
request.expected_status,
request.expected_revision,
request.expected_attempt,
task["metadata_json"],
task["required_capabilities_json"],
task["result_json"],
task["verification_json"],
),
)
if cursor.rowcount != 1:
raise InitialExecutionProjectionError(
"SOURCE_CAPTURE_PROJECTION_RECOVERY_CAS_MISMATCH", 409
)
event_payload = {
"schema": "aios.task-initial-execution-projection.event.v1",
"task_id": task_id,
"job_id": request.expected_job_id,
"authorization_id": request.authorization_id,
"idempotency_key": request.idempotency_key,
"reason": request.reason,
"action": "agent_runtime",
"adapter": request.adapter,
"required_capability": request.required_capability,
"source_capture_execution_digest": request.expected_source_capture_execution_digest,
"source_capture_result_sha256": request.expected_source_capture_result_sha256,
"previous_verification_sha256": request.previous_verification_sha256,
"execution_digest": execution["execution_digest"],
"previous_revision": request.expected_revision,
"applied_revision": applied_revision,
"attempt": request.expected_attempt,
"request_sha256": request_sha,
"recovery_from_source_capture": True,
"writer_lease": False,
"main_write": False,
"production_write": False,
}
self._insert_event(connection, task_id, event_payload)
connection.execute(
"""
INSERT INTO task_source_capture_projection_recoveries(
task_id,idempotency_key,request_sha256,authorization_id,
expected_job_id,source_capture_result_sha256,
source_capture_execution_digest,execution_digest,
required_capability,adapter,expected_revision,applied_revision,
attempt,created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
task_id,
request.idempotency_key,
request_sha,
request.authorization_id,
request.expected_job_id,
request.expected_source_capture_result_sha256,
request.expected_source_capture_execution_digest,
execution["execution_digest"],
request.required_capability,
request.adapter,
request.expected_revision,
applied_revision,
request.expected_attempt,
_now(),
),
)
connection.commit()
return {
"task_id": task_id,
"status": "QUEUED",
"attempt": request.expected_attempt,
"revision": applied_revision,
"job_id": request.expected_job_id,
"source_capture_execution_digest": request.expected_source_capture_execution_digest,
"execution_digest": execution["execution_digest"],
"required_capability": request.required_capability,
"adapter": request.adapter,
"authorization_id": request.authorization_id,
"recovery_from_source_capture": True,
"idempotent": False,
}
class InitialExecutionProjectionHandler(BaseHTTPRequestHandler):
store: InitialExecutionProjectionStore | None = None
@@ -666,8 +1201,13 @@ class InitialExecutionProjectionHandler(BaseHTTPRequestHandler):
self._json({"error": "NOT_FOUND"}, 404)
def do_POST(self) -> None:
match = re.fullmatch(r"/v1/tasks/([^/]+)/initial-execution-projection", self.path)
if match is None:
initial_match = re.fullmatch(
r"/v1/tasks/([^/]+)/initial-execution-projection", self.path
)
recovery_match = re.fullmatch(
r"/v1/tasks/([^/]+)/source-capture-projection-recovery", self.path
)
if initial_match is None and recovery_match is None:
self._json({"error": "NOT_FOUND"}, 404)
return
if not self._authorized():
@@ -685,10 +1225,18 @@ class InitialExecutionProjectionHandler(BaseHTTPRequestHandler):
value = json.loads(self.rfile.read(length).decode("utf-8"))
if not isinstance(value, Mapping):
raise ValueError
request = InitialExecutionProjectionRequest.from_mapping(value)
if self.store is None:
raise InitialExecutionProjectionError("RUNTIME_UNAVAILABLE", 503)
result = self.store.project(match.group(1), request)
if initial_match is not None:
request = InitialExecutionProjectionRequest.from_mapping(value)
result = self.store.project(initial_match.group(1), request)
else:
recovery_request = SourceCaptureProjectionRecoveryRequest.from_mapping(
value
)
result = self.store.project_from_source_capture(
recovery_match.group(1), recovery_request
)
self._json(result, 200 if result["idempotent"] else 201)
except InitialExecutionProjectionError as exc:
self._json({"error": exc.code}, exc.status)
@@ -0,0 +1,870 @@
#!/usr/bin/env python3
"""Durable online task protocol for external AIOS agents.
The store is deliberately independent from Git/Gitea mutation. It provides a
small durable state machine for publishing work, atomic claim/lease ownership,
heartbeat, idempotent result submission, explicit server-side verification,
lease recovery, and audit events.
No plaintext agent credential or task lease token is persisted.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import hashlib
import hmac
import json
from pathlib import Path
import secrets
import sqlite3
from typing import Any, Callable, Iterable, Mapping
import uuid
from aios_capability_contract import has_capabilities
from aios_online_agent_execution import (
ExecutionProjectionError,
validate_execution_envelope,
)
TERMINAL_STATES = frozenset({"SUCCEEDED", "FAILED", "EXPIRED", "CANCELLED"})
ACTIVE_LEASE_STATES = frozenset({"CLAIMED", "RUNNING"})
TASK_STATES = frozenset({
"QUEUED", "CLAIMED", "RUNNING", "VERIFYING",
"SUCCEEDED", "FAILED", "EXPIRED", "CANCELLED",
})
VERIFY_DECISIONS = frozenset({"SUCCEEDED", "NEEDS_REWORK", "FAILED"})
GOVERNED_WRITE_CAPABILITIES = frozenset({
"governed.write.branch",
"governed.write.file",
})
EXECUTION_PROJECTION_SCHEMA = "aios.online-agent.task-projection.v1"
def _validate_write_execution_binding(
task_id: str,
required_capabilities: list[str],
metadata: Mapping[str, Any],
) -> None:
required = set(required_capabilities)
if not required.intersection(GOVERNED_WRITE_CAPABILITIES):
return
execution = metadata.get("execution")
projection = metadata.get("execution_projection")
if not isinstance(execution, Mapping) or not isinstance(projection, Mapping):
raise TaskProtocolError("GOVERNED_EXECUTION_BINDING_REQUIRED", 409)
try:
canonical = validate_execution_envelope(dict(execution))
except (ExecutionProjectionError, TypeError, ValueError, KeyError) as exc:
raise TaskProtocolError("EXECUTION_ENVELOPE_INVALID", 409) from exc
if canonical.get("task_id") != task_id:
raise TaskProtocolError("TASK_BINDING_CONFLICT", 409)
if set(canonical.get("capability_scope") or ()) != required:
raise TaskProtocolError("EXECUTION_CAPABILITY_SCOPE_CONFLICT", 409)
if (
projection.get("schema") != EXECUTION_PROJECTION_SCHEMA
or projection.get("task_id") != task_id
or projection.get("job_id") != canonical.get("job_id")
or projection.get("execution_digest") != canonical.get("execution_digest")
):
raise TaskProtocolError("EXECUTION_PROJECTION_INVALID", 409)
class TaskProtocolError(RuntimeError):
def __init__(self, code: str, http_status: int = 409) -> None:
super().__init__(code)
self.code = code
self.http_status = http_status
@dataclass(frozen=True, slots=True)
class Principal:
kind: str
identifier: str
def utcnow() -> datetime:
return datetime.now(timezone.utc)
def iso(value: datetime) -> str:
return value.astimezone(timezone.utc).isoformat()
def parse_time(value: str | None) -> datetime | None:
if not value:
return None
parsed = datetime.fromisoformat(value)
if parsed.tzinfo is None:
parsed = parsed.replace(tzinfo=timezone.utc)
return parsed.astimezone(timezone.utc)
def canonical_json(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def digest_json(value: Any) -> str:
return hashlib.sha256(canonical_json(value).encode("utf-8")).hexdigest()
def _string_list(value: Iterable[object] | None, field: str, *, max_items: int = 128) -> list[str]:
if value is None:
return []
if isinstance(value, (str, bytes)) or not isinstance(value, Iterable):
raise TaskProtocolError(field.upper() + "_INVALID", 400)
result: list[str] = []
for item in value:
text = str(item).strip()
if not text or len(text) > 160:
raise TaskProtocolError(field.upper() + "_INVALID", 400)
result.append(text)
if len(result) > max_items:
raise TaskProtocolError(field.upper() + "_TOO_LARGE", 400)
return sorted(set(result))
def _json_object(value: object | None, field: str, *, max_bytes: int = 128 * 1024) -> dict[str, Any]:
if value is None:
return {}
if not isinstance(value, Mapping):
raise TaskProtocolError(field.upper() + "_INVALID", 400)
result = dict(value)
if len(canonical_json(result).encode("utf-8")) > max_bytes:
raise TaskProtocolError(field.upper() + "_TOO_LARGE", 413)
return result
def _criteria(value: object | None) -> list[Any]:
if value is None:
return []
if isinstance(value, (str, bytes)) or not isinstance(value, list):
raise TaskProtocolError("SUCCESS_CRITERIA_INVALID", 400)
if len(value) > 128:
raise TaskProtocolError("SUCCESS_CRITERIA_TOO_LARGE", 413)
if len(canonical_json(value).encode("utf-8")) > 128 * 1024:
raise TaskProtocolError("SUCCESS_CRITERIA_TOO_LARGE", 413)
return list(value)
class TaskStore:
"""SQLite-backed durable task store.
SQLite BEGIN IMMEDIATE serializes claims so two agents cannot successfully
claim the same queued task. WAL mode allows concurrent readers.
"""
def __init__(
self,
path: str | Path,
*,
now: Callable[[], datetime] = utcnow,
token_pepper: str = "",
) -> None:
self.path = Path(path)
self.path.parent.mkdir(parents=True, exist_ok=True)
self.now = now
self.token_pepper = token_pepper
self._initialize()
def _connect(self) -> sqlite3.Connection:
connection = sqlite3.connect(self.path, timeout=10, isolation_level=None)
connection.row_factory = sqlite3.Row
connection.execute("PRAGMA foreign_keys=ON")
connection.execute("PRAGMA journal_mode=WAL")
connection.execute("PRAGMA synchronous=FULL")
connection.execute("PRAGMA busy_timeout=10000")
return connection
def _initialize(self) -> None:
with self._connect() as connection:
connection.executescript(
"""
CREATE TABLE IF NOT EXISTS agents (
agent_id TEXT PRIMARY KEY,
agent_type TEXT NOT NULL,
agent_version TEXT NOT NULL,
capabilities_json TEXT NOT NULL,
metadata_json TEXT NOT NULL,
token_hash TEXT NOT NULL UNIQUE,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
task_id TEXT PRIMARY KEY,
goal TEXT NOT NULL,
instructions TEXT NOT NULL,
success_criteria_json TEXT NOT NULL,
required_capabilities_json TEXT NOT NULL,
constraints_json TEXT NOT NULL,
metadata_json TEXT NOT NULL,
deadline TEXT,
priority INTEGER NOT NULL,
max_attempts INTEGER NOT NULL,
attempt INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL,
claimed_by TEXT,
lease_token_hash TEXT,
lease_expires_at TEXT,
result_json TEXT,
result_digest TEXT,
result_idempotency_key TEXT,
verification_json TEXT,
revision INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(claimed_by) REFERENCES agents(agent_id)
);
CREATE TABLE IF NOT EXISTS idempotency (
scope TEXT NOT NULL,
key TEXT NOT NULL,
payload_digest TEXT NOT NULL,
response_json TEXT NOT NULL,
created_at TEXT NOT NULL,
PRIMARY KEY(scope, key)
);
CREATE TABLE IF NOT EXISTS events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT,
agent_id TEXT,
event_type TEXT NOT NULL,
payload_json TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_tasks_queue
ON tasks(status, priority DESC, created_at);
CREATE INDEX IF NOT EXISTS idx_tasks_lease
ON tasks(status, lease_expires_at);
CREATE INDEX IF NOT EXISTS idx_events_task
ON events(task_id, event_id);
"""
)
def _token_hash(self, token: str) -> str:
return hashlib.sha256((self.token_pepper + "\0" + token).encode("utf-8")).hexdigest()
@staticmethod
def _row_task(row: sqlite3.Row, *, include_result: bool = True) -> dict[str, Any]:
value: dict[str, Any] = {
"task_id": row["task_id"],
"goal": row["goal"],
"instructions": row["instructions"],
"success_criteria": json.loads(row["success_criteria_json"]),
"required_capabilities": json.loads(row["required_capabilities_json"]),
"constraints": json.loads(row["constraints_json"]),
"metadata": json.loads(row["metadata_json"]),
"deadline": row["deadline"],
"priority": row["priority"],
"max_attempts": row["max_attempts"],
"attempt": row["attempt"],
"status": row["status"],
"claimed_by": row["claimed_by"],
"lease_expires_at": row["lease_expires_at"],
"revision": row["revision"],
"created_at": row["created_at"],
"updated_at": row["updated_at"],
}
if include_result:
value["result"] = json.loads(row["result_json"]) if row["result_json"] else None
value["verification"] = (
json.loads(row["verification_json"]) if row["verification_json"] else None
)
return value
def _event(
self,
connection: sqlite3.Connection,
event_type: str,
*,
task_id: str | None = None,
agent_id: str | None = None,
payload: Mapping[str, Any] | None = None,
) -> None:
connection.execute(
"INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at)"
" VALUES(?,?,?,?,?)",
(task_id, agent_id, event_type, canonical_json(dict(payload or {})), iso(self.now())),
)
def register_agent(
self,
*,
agent_id: str,
agent_type: str,
agent_version: str,
capabilities: Iterable[object],
metadata: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
agent_id = agent_id.strip()
agent_type = agent_type.strip()
agent_version = agent_version.strip()
if not agent_id or len(agent_id) > 160 or not agent_type or not agent_version:
raise TaskProtocolError("AGENT_IDENTITY_INVALID", 400)
caps = _string_list(capabilities, "capabilities")
meta = _json_object(metadata, "metadata")
token = "agt_" + secrets.token_urlsafe(32)
now = iso(self.now())
try:
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
connection.execute(
"INSERT INTO agents(agent_id,agent_type,agent_version,capabilities_json,"
"metadata_json,token_hash,status,created_at,last_seen_at)"
" VALUES(?,?,?,?,?,?,?,?,?)",
(
agent_id,
agent_type,
agent_version,
canonical_json(caps),
canonical_json(meta),
self._token_hash(token),
"ACTIVE",
now,
now,
),
)
self._event(
connection,
"AGENT_REGISTERED",
agent_id=agent_id,
payload={"agent_type": agent_type, "agent_version": agent_version},
)
connection.commit()
except sqlite3.IntegrityError as exc:
raise TaskProtocolError("AGENT_ALREADY_EXISTS", 409) from exc
return {
"agent_id": agent_id,
"agent_type": agent_type,
"agent_version": agent_version,
"capabilities": caps,
"agent_token": token,
"credential_returned_once": True,
}
def authenticate_agent(self, token: str) -> dict[str, Any]:
if not token:
raise TaskProtocolError("UNAUTHORIZED", 401)
token_hash = self._token_hash(token)
with self._connect() as connection:
row = connection.execute(
"SELECT * FROM agents WHERE token_hash=? AND status='ACTIVE'",
(token_hash,),
).fetchone()
if row is None:
raise TaskProtocolError("UNAUTHORIZED", 401)
connection.execute(
"UPDATE agents SET last_seen_at=? WHERE agent_id=?",
(iso(self.now()), row["agent_id"]),
)
return {
"agent_id": row["agent_id"],
"agent_type": row["agent_type"],
"agent_version": row["agent_version"],
"capabilities": json.loads(row["capabilities_json"]),
"metadata": json.loads(row["metadata_json"]),
}
def _idempotent_lookup(
self,
connection: sqlite3.Connection,
*,
scope: str,
key: str,
payload_digest: str,
) -> dict[str, Any] | None:
row = connection.execute(
"SELECT payload_digest,response_json FROM idempotency WHERE scope=? AND key=?",
(scope, key),
).fetchone()
if row is None:
return None
if not hmac.compare_digest(row["payload_digest"], payload_digest):
raise TaskProtocolError("IDEMPOTENCY_CONFLICT", 409)
return json.loads(row["response_json"])
def _idempotent_store(
self,
connection: sqlite3.Connection,
*,
scope: str,
key: str,
payload_digest: str,
response: Mapping[str, Any],
) -> None:
connection.execute(
"INSERT INTO idempotency(scope,key,payload_digest,response_json,created_at)"
" VALUES(?,?,?,?,?)",
(scope, key, payload_digest, canonical_json(dict(response)), iso(self.now())),
)
def create_task(
self,
*,
task_id: str | None,
goal: str,
instructions: str,
success_criteria: object | None,
required_capabilities: Iterable[object] | None = None,
constraints: Mapping[str, Any] | None = None,
metadata: Mapping[str, Any] | None = None,
deadline: str | None = None,
priority: int = 0,
max_attempts: int = 3,
idempotency_key: str,
) -> dict[str, Any]:
task_id = (task_id or ("task-" + uuid.uuid4().hex)).strip()
goal = goal.strip()
instructions = instructions.strip()
idempotency_key = idempotency_key.strip()
if not task_id or len(task_id) > 180 or not goal or not instructions:
raise TaskProtocolError("TASK_BINDING_INVALID", 400)
if not idempotency_key or len(idempotency_key) > 160:
raise TaskProtocolError("IDEMPOTENCY_KEY_INVALID", 400)
if not isinstance(priority, int) or isinstance(priority, bool) or not -100 <= priority <= 100:
raise TaskProtocolError("PRIORITY_INVALID", 400)
if not isinstance(max_attempts, int) or isinstance(max_attempts, bool) or not 1 <= max_attempts <= 20:
raise TaskProtocolError("MAX_ATTEMPTS_INVALID", 400)
parsed_deadline = parse_time(deadline)
if parsed_deadline and parsed_deadline <= self.now():
raise TaskProtocolError("DEADLINE_INVALID", 400)
criteria = _criteria(success_criteria)
required = _string_list(required_capabilities, "required_capabilities")
constraints_value = _json_object(constraints, "constraints")
metadata_value = _json_object(metadata, "metadata")
_validate_write_execution_binding(task_id, required, metadata_value)
payload = {
"task_id": task_id,
"goal": goal,
"instructions": instructions,
"success_criteria": criteria,
"required_capabilities": required,
"constraints": constraints_value,
"metadata": metadata_value,
"deadline": iso(parsed_deadline) if parsed_deadline else None,
"priority": priority,
"max_attempts": max_attempts,
}
payload_digest = digest_json(payload)
now = iso(self.now())
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
replay = self._idempotent_lookup(
connection,
scope="create_task",
key=idempotency_key,
payload_digest=payload_digest,
)
if replay is not None:
connection.commit()
return {**replay, "idempotent": True}
try:
connection.execute(
"INSERT INTO tasks(task_id,goal,instructions,success_criteria_json,"
"required_capabilities_json,constraints_json,metadata_json,deadline,priority,"
"max_attempts,attempt,status,revision,created_at,updated_at)"
" VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
task_id,
goal,
instructions,
canonical_json(criteria),
canonical_json(required),
canonical_json(constraints_value),
canonical_json(metadata_value),
payload["deadline"],
priority,
max_attempts,
0,
"QUEUED",
1,
now,
now,
),
)
except sqlite3.IntegrityError as exc:
raise TaskProtocolError("TASK_ID_CONFLICT", 409) from exc
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
result = self._row_task(row)
self._idempotent_store(
connection,
scope="create_task",
key=idempotency_key,
payload_digest=payload_digest,
response=result,
)
self._event(connection, "TASK_CREATED", task_id=task_id, payload={"priority": priority})
connection.commit()
return result
def _expire_locked(self, connection: sqlite3.Connection) -> None:
now_dt = self.now()
now = iso(now_dt)
rows = connection.execute(
"SELECT * FROM tasks WHERE status IN ('QUEUED','CLAIMED','RUNNING')"
).fetchall()
for row in rows:
deadline = parse_time(row["deadline"])
if deadline is not None and deadline <= now_dt:
connection.execute(
"UPDATE tasks SET status='EXPIRED',lease_token_hash=NULL,"
"lease_expires_at=NULL,revision=revision+1,updated_at=? WHERE task_id=?",
(now, row["task_id"]),
)
self._event(connection, "TASK_EXPIRED", task_id=row["task_id"])
continue
if row["status"] in ACTIVE_LEASE_STATES:
lease_expires = parse_time(row["lease_expires_at"])
if lease_expires is not None and lease_expires <= now_dt:
if row["attempt"] >= row["max_attempts"]:
next_state = "FAILED"
else:
next_state = "QUEUED"
if next_state == "FAILED":
connection.execute(
"UPDATE tasks SET status=?,lease_token_hash=NULL,"
"lease_expires_at=NULL,revision=revision+1,updated_at=? WHERE task_id=?",
(next_state, now, row["task_id"]),
)
else:
connection.execute(
"UPDATE tasks SET status=?,claimed_by=NULL,lease_token_hash=NULL,"
"lease_expires_at=NULL,revision=revision+1,updated_at=? WHERE task_id=?",
(next_state, now, row["task_id"]),
)
self._event(
connection,
"LEASE_EXPIRED",
task_id=row["task_id"],
agent_id=row["claimed_by"],
payload={"next_state": next_state, "attempt": row["attempt"]},
)
def expire_leases(self) -> int:
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
before = connection.total_changes
self._expire_locked(connection)
changed = connection.total_changes - before
connection.commit()
return changed
def poll_tasks(self, *, agent_id: str, limit: int = 20) -> list[dict[str, Any]]:
if not 1 <= int(limit) <= 100:
raise TaskProtocolError("LIMIT_INVALID", 400)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._expire_locked(connection)
agent = connection.execute(
"SELECT capabilities_json FROM agents WHERE agent_id=? AND status='ACTIVE'",
(agent_id,),
).fetchone()
if agent is None:
raise TaskProtocolError("AGENT_NOT_FOUND", 404)
capabilities = json.loads(agent["capabilities_json"])
rows = connection.execute(
"SELECT * FROM tasks WHERE status='QUEUED' ORDER BY priority DESC,created_at ASC"
).fetchall()
result = []
for row in rows:
required = json.loads(row["required_capabilities_json"])
if has_capabilities(capabilities, required):
result.append(self._row_task(row, include_result=False))
if len(result) >= int(limit):
break
connection.commit()
return result
def _agent_capabilities(self, connection: sqlite3.Connection, agent_id: str) -> set[str]:
row = connection.execute(
"SELECT capabilities_json FROM agents WHERE agent_id=? AND status='ACTIVE'",
(agent_id,),
).fetchone()
if row is None:
raise TaskProtocolError("AGENT_NOT_FOUND", 404)
return set(json.loads(row["capabilities_json"]))
def claim_task(
self,
*,
task_id: str,
agent_id: str,
lease_seconds: int = 300,
) -> dict[str, Any]:
if not isinstance(lease_seconds, int) or isinstance(lease_seconds, bool) or not 30 <= lease_seconds <= 3600:
raise TaskProtocolError("LEASE_SECONDS_INVALID", 400)
lease_token = "lease_" + secrets.token_urlsafe(32)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._expire_locked(connection)
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
if row["status"] != "QUEUED":
raise TaskProtocolError("TASK_NOT_CLAIMABLE", 409)
capabilities = self._agent_capabilities(connection, agent_id)
required = json.loads(row["required_capabilities_json"])
if not has_capabilities(capabilities, required):
raise TaskProtocolError("AGENT_CAPABILITY_MISMATCH", 403)
attempt = int(row["attempt"]) + 1
if attempt > int(row["max_attempts"]):
raise TaskProtocolError("TASK_ATTEMPTS_EXHAUSTED", 409)
expires = self.now() + timedelta(seconds=lease_seconds)
now = iso(self.now())
updated = connection.execute(
"UPDATE tasks SET status='CLAIMED',claimed_by=?,lease_token_hash=?,"
"lease_expires_at=?,attempt=?,revision=revision+1,updated_at=? "
"WHERE task_id=? AND status='QUEUED'",
(
agent_id,
self._token_hash(lease_token),
iso(expires),
attempt,
now,
task_id,
),
)
if updated.rowcount != 1:
raise TaskProtocolError("TASK_CLAIM_CONFLICT", 409)
self._event(
connection,
"TASK_CLAIMED",
task_id=task_id,
agent_id=agent_id,
payload={"attempt": attempt, "lease_expires_at": iso(expires)},
)
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
connection.commit()
return {
**self._row_task(row, include_result=False),
"lease_token": lease_token,
"lease_token_returned_once": True,
}
def _validate_lease(
self,
connection: sqlite3.Connection,
*,
task_id: str,
agent_id: str,
lease_token: str,
allowed_states: frozenset[str] = ACTIVE_LEASE_STATES,
) -> sqlite3.Row:
self._expire_locked(connection)
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
if row["status"] not in allowed_states:
raise TaskProtocolError("TASK_LEASE_NOT_ACTIVE", 409)
if row["claimed_by"] != agent_id:
raise TaskProtocolError("TASK_LEASE_OWNER_MISMATCH", 403)
if not row["lease_token_hash"] or not hmac.compare_digest(
row["lease_token_hash"], self._token_hash(lease_token)
):
raise TaskProtocolError("TASK_LEASE_TOKEN_INVALID", 403)
expires = parse_time(row["lease_expires_at"])
if expires is None or expires <= self.now():
raise TaskProtocolError("TASK_LEASE_EXPIRED", 409)
return row
def heartbeat(
self,
*,
task_id: str,
agent_id: str,
lease_token: str,
progress: Mapping[str, Any] | None = None,
lease_seconds: int = 300,
) -> dict[str, Any]:
if not 30 <= int(lease_seconds) <= 3600:
raise TaskProtocolError("LEASE_SECONDS_INVALID", 400)
progress_value = _json_object(progress, "progress", max_bytes=64 * 1024)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._validate_lease(
connection,
task_id=task_id,
agent_id=agent_id,
lease_token=lease_token,
)
expires = self.now() + timedelta(seconds=int(lease_seconds))
now = iso(self.now())
connection.execute(
"UPDATE tasks SET status='RUNNING',lease_expires_at=?,revision=revision+1,"
"updated_at=? WHERE task_id=?",
(iso(expires), now, task_id),
)
self._event(
connection,
"TASK_HEARTBEAT",
task_id=task_id,
agent_id=agent_id,
payload={"progress": progress_value, "lease_expires_at": iso(expires)},
)
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
connection.commit()
return self._row_task(row, include_result=False)
def submit_result(
self,
*,
task_id: str,
agent_id: str,
lease_token: str,
idempotency_key: str,
result: Mapping[str, Any],
) -> dict[str, Any]:
idempotency_key = idempotency_key.strip()
if not idempotency_key or len(idempotency_key) > 160:
raise TaskProtocolError("IDEMPOTENCY_KEY_INVALID", 400)
result_value = _json_object(result, "result", max_bytes=1024 * 1024)
result_digest = digest_json(result_value)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
if row["result_idempotency_key"] == idempotency_key:
if not row["result_digest"] or not hmac.compare_digest(row["result_digest"], result_digest):
raise TaskProtocolError("IDEMPOTENCY_CONFLICT", 409)
connection.commit()
return {**self._row_task(row), "idempotent": True}
self._validate_lease(
connection,
task_id=task_id,
agent_id=agent_id,
lease_token=lease_token,
)
now = iso(self.now())
connection.execute(
"UPDATE tasks SET status='VERIFYING',result_json=?,result_digest=?,"
"result_idempotency_key=?,lease_token_hash=NULL,lease_expires_at=NULL,"
"revision=revision+1,updated_at=? WHERE task_id=?",
(
canonical_json(result_value),
result_digest,
idempotency_key,
now,
task_id,
),
)
self._event(
connection,
"RESULT_SUBMITTED",
task_id=task_id,
agent_id=agent_id,
payload={"result_digest": result_digest, "idempotency_key": idempotency_key},
)
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
connection.commit()
return self._row_task(row)
def verify_task(
self,
*,
task_id: str,
decision: str,
verification: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
decision = decision.strip().upper()
if decision not in VERIFY_DECISIONS:
raise TaskProtocolError("VERIFICATION_DECISION_INVALID", 400)
verification_value = _json_object(verification, "verification")
verification_value = {"decision": decision, **verification_value}
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
if row["status"] != "VERIFYING":
raise TaskProtocolError("TASK_NOT_VERIFYING", 409)
if decision == "SUCCEEDED":
next_state = "SUCCEEDED"
elif decision == "FAILED":
next_state = "FAILED"
elif int(row["attempt"]) >= int(row["max_attempts"]):
next_state = "FAILED"
verification_value["reason"] = verification_value.get(
"reason", "MAX_ATTEMPTS_EXHAUSTED"
)
else:
next_state = "QUEUED"
now = iso(self.now())
if next_state in {"SUCCEEDED", "FAILED"}:
connection.execute(
"UPDATE tasks SET status=?,lease_token_hash=NULL,"
"lease_expires_at=NULL,verification_json=?,revision=revision+1,updated_at=? "
"WHERE task_id=?",
(next_state, canonical_json(verification_value), now, task_id),
)
else:
connection.execute(
"UPDATE tasks SET status=?,claimed_by=NULL,lease_token_hash=NULL,"
"lease_expires_at=NULL,verification_json=?,revision=revision+1,updated_at=? "
"WHERE task_id=?",
(next_state, canonical_json(verification_value), now, task_id),
)
self._event(
connection,
"TASK_VERIFIED",
task_id=task_id,
agent_id=row["claimed_by"],
payload={"decision": decision, "next_state": next_state},
)
updated = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
connection.commit()
return self._row_task(updated)
def cancel_task(self, *, task_id: str, reason: str = "") -> dict[str, Any]:
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
if row["status"] in TERMINAL_STATES:
connection.commit()
return self._row_task(row)
connection.execute(
"UPDATE tasks SET status='CANCELLED',lease_token_hash=NULL,"
"lease_expires_at=NULL,revision=revision+1,updated_at=? WHERE task_id=?",
(iso(self.now()), task_id),
)
self._event(connection, "TASK_CANCELLED", task_id=task_id, payload={"reason": reason})
updated = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
connection.commit()
return self._row_task(updated)
def get_task(self, task_id: str) -> dict[str, Any]:
self.expire_leases()
with self._connect() as connection:
row = connection.execute("SELECT * FROM tasks WHERE task_id=?", (task_id,)).fetchone()
if row is None:
raise TaskProtocolError("TASK_NOT_FOUND", 404)
return self._row_task(row)
def get_events(self, task_id: str, *, limit: int = 200) -> list[dict[str, Any]]:
with self._connect() as connection:
rows = connection.execute(
"SELECT * FROM events WHERE task_id=? ORDER BY event_id DESC LIMIT ?",
(task_id, max(1, min(int(limit), 1000))),
).fetchall()
return [
{
"event_id": row["event_id"],
"task_id": row["task_id"],
"agent_id": row["agent_id"],
"event_type": row["event_type"],
"payload": json.loads(row["payload_json"]),
"created_at": row["created_at"],
}
for row in reversed(rows)
]
@@ -0,0 +1,655 @@
"""CAS-bound projection from an unprivileged planner to a host coordinator."""
from __future__ import annotations
from dataclasses import dataclass
import hmac
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import os
from pathlib import Path
import re
import sqlite3
from typing import Any, Callable, Mapping, Sequence
import urllib.error
import urllib.request
from aios_agent_task_initial_execution_projection import (
InitialExecutionProjectionError,
InitialExecutionProjectionStore,
JOB_RE,
KEY_RE,
MAX_BODY,
SHA_RE,
TASK_RE,
_canonical,
_now,
_required_authorization_id,
_require_string,
_sha,
load_control_token,
)
from aios_online_agent_execution import (
GovernedExecutionBinding,
validate_execution_envelope,
)
RUNTIME_CAPABILITY = "agent.runtime.openclaw"
COORDINATOR_CAPABILITY = "engineering.autonomous"
COORDINATOR_CAPABILITIES = [RUNTIME_CAPABILITY, COORDINATOR_CAPABILITY]
@dataclass(frozen=True)
class SupervisorCoordinatorProjectionRequest:
expected_status: str
expected_revision: int
expected_attempt: int
expected_metadata_sha256: str
expected_required_capabilities_sha256: str
previous_verification_sha256: str
expected_planner_result_sha256: str
expected_old_execution_digest: str
expected_new_execution_digest: str
expected_job_id: str
authorization_id: str
idempotency_key: str
reason: str
@classmethod
def from_mapping(
cls, value: Mapping[str, Any]
) -> "SupervisorCoordinatorProjectionRequest":
allowed = {
"expected_status",
"expected_revision",
"expected_attempt",
"expected_metadata_sha256",
"expected_required_capabilities_sha256",
"previous_verification_sha256",
"expected_planner_result_sha256",
"expected_old_execution_digest",
"expected_new_execution_digest",
"expected_job_id",
"authorization_id",
"idempotency_key",
"reason",
}
if set(value) != allowed:
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_PROJECTION_FIELDS_INVALID", 400
)
status = _require_string(value["expected_status"], "EXPECTED_STATUS_INVALID")
revision = value["expected_revision"]
attempt = value["expected_attempt"]
if (
status != "QUEUED"
or not isinstance(revision, int)
or isinstance(revision, bool)
or revision < 2
or not isinstance(attempt, int)
or isinstance(attempt, bool)
or attempt < 1
):
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_PROJECTION_STATE_INVALID", 400
)
names = (
"expected_metadata_sha256",
"expected_required_capabilities_sha256",
"previous_verification_sha256",
"expected_planner_result_sha256",
"expected_old_execution_digest",
"expected_new_execution_digest",
)
digests = {
name: _require_string(value[name], name.upper() + "_INVALID", SHA_RE)
for name in names
}
job_id = _require_string(value["expected_job_id"], "JOB_ID_INVALID", JOB_RE)
authorization_id = _require_string(
value["authorization_id"], "AUTHORIZATION_ID_INVALID", KEY_RE
)
idempotency_key = _require_string(
value["idempotency_key"], "IDEMPOTENCY_KEY_INVALID", KEY_RE
)
reason = _require_string(value["reason"], "PROJECTION_REASON_INVALID")
if len(reason) > 512:
raise InitialExecutionProjectionError("PROJECTION_REASON_INVALID", 400)
return cls(
status,
revision,
attempt,
digests["expected_metadata_sha256"],
digests["expected_required_capabilities_sha256"],
digests["previous_verification_sha256"],
digests["expected_planner_result_sha256"],
digests["expected_old_execution_digest"],
digests["expected_new_execution_digest"],
job_id,
authorization_id,
idempotency_key,
reason,
)
def public(self) -> dict[str, Any]:
return {
"expected_status": self.expected_status,
"expected_revision": self.expected_revision,
"expected_attempt": self.expected_attempt,
"expected_metadata_sha256": self.expected_metadata_sha256,
"expected_required_capabilities_sha256": self.expected_required_capabilities_sha256,
"previous_verification_sha256": self.previous_verification_sha256,
"expected_planner_result_sha256": self.expected_planner_result_sha256,
"expected_old_execution_digest": self.expected_old_execution_digest,
"expected_new_execution_digest": self.expected_new_execution_digest,
"expected_job_id": self.expected_job_id,
"authorization_id": self.authorization_id,
"idempotency_key": self.idempotency_key,
"reason": self.reason,
}
class CanonicalCoordinatorExecutionResolver:
def __init__(self, base_url: str, control_token: str) -> None:
self.base_url = base_url.rstrip("/")
self.control_token = control_token
def __call__(
self,
task_id: str,
expected_job_id: str,
runtime_input: Mapping[str, Any],
) -> dict[str, Any]:
body = {
"task_id": task_id,
"job_id": expected_job_id,
"required_capabilities": COORDINATOR_CAPABILITIES,
"execution": {
"action": "agent_runtime",
"adapter": "openclaw",
"capability_scope": COORDINATOR_CAPABILITIES,
"runtime_input": dict(runtime_input),
},
}
request = urllib.request.Request(
self.base_url + "/v2/execution-envelope",
method="POST",
data=_canonical(body),
headers={
"Authorization": "Bearer " + self.control_token,
"Content-Type": "application/json",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(request, timeout=15) as response:
raw, status = response.read(), response.status
except urllib.error.HTTPError as exc:
raw, status = exc.read(), exc.code
try:
value = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise InitialExecutionProjectionError(
"CANONICAL_COORDINATOR_EXECUTION_RESPONSE_INVALID", 502
) from exc
if not isinstance(value, Mapping):
raise InitialExecutionProjectionError(
"CANONICAL_COORDINATOR_EXECUTION_RESPONSE_INVALID", 502
)
if status != 200:
raise InitialExecutionProjectionError(
str(value.get("error") or "CANONICAL_COORDINATOR_EXECUTION_FAILED"),
502 if status >= 500 else 409,
)
return dict(value)
class SupervisorCoordinatorProjectionStore(InitialExecutionProjectionStore):
def __init__(
self,
db_path: Path,
resolver: Callable[[str, str, Mapping[str, Any]], Mapping[str, Any]],
) -> None:
super().__init__(db_path, resolver) # type: ignore[arg-type]
self.coordinator_resolver = resolver
@staticmethod
def _ensure_table(connection: sqlite3.Connection) -> None:
connection.execute(
"""
CREATE TABLE IF NOT EXISTS task_supervisor_coordinator_projections (
task_id TEXT NOT NULL,
idempotency_key TEXT NOT NULL,
request_sha256 TEXT NOT NULL,
authorization_id TEXT NOT NULL,
expected_job_id TEXT NOT NULL,
planner_result_sha256 TEXT NOT NULL,
old_execution_digest TEXT NOT NULL,
new_execution_digest TEXT NOT NULL,
expected_revision INTEGER NOT NULL,
applied_revision INTEGER NOT NULL,
attempt INTEGER NOT NULL,
created_at TEXT NOT NULL,
PRIMARY KEY(task_id,idempotency_key)
)
"""
)
@classmethod
def _planner_event_chain(
cls, connection: sqlite3.Connection, task_id: str
) -> None:
table, columns = cls._event_table(connection)
ordering = "event_id" if "event_id" in columns else "rowid"
rows = connection.execute(
f'SELECT event_type,payload_json FROM "{table}" '
f'WHERE task_id=? ORDER BY {ordering}',
(task_id,),
).fetchall()
events = [str(row[0]) for row in rows]
try:
projection = len(events) - 1 - events[::-1].index(
"TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZED"
)
except ValueError as exc:
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_PROJECTION_EVENT_MISSING", 409
) from exc
tail = events[projection + 1 :]
if (
len(tail) < 4
or tail[0] != "TASK_CLAIMED"
or tail.count("TASK_CLAIMED") != 1
or tail.count("RESULT_SUBMITTED") != 1
or tail.count("TASK_VERIFIED") != 1
or tail[-1] != "TASK_VERIFIED"
):
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_EVENT_CHAIN_INVALID", 409
)
result = tail.index("RESULT_SUBMITTED")
if (
not any(event == "TASK_HEARTBEAT" for event in tail[1:result])
or any(
event not in {
"TASK_CLAIMED",
"TASK_HEARTBEAT",
"RESULT_SUBMITTED",
"TASK_VERIFIED",
}
for event in tail
)
):
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_EVENT_CHAIN_INVALID", 409
)
verification_event = json.loads(rows[-1][1])
if (
verification_event.get("decision") != "NEEDS_REWORK"
or verification_event.get("next_state") != "QUEUED"
):
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_NEEDS_REWORK_EVENT_REQUIRED", 409
)
def _load(
self,
connection: sqlite3.Connection,
task_id: str,
request: SupervisorCoordinatorProjectionRequest,
) -> tuple[sqlite3.Row, dict[str, Any], dict[str, Any]]:
task = connection.execute(
"SELECT * FROM tasks WHERE task_id=?", (task_id,)
).fetchone()
if task is None:
raise InitialExecutionProjectionError("TASK_NOT_FOUND", 404)
if (
task["status"] != request.expected_status
or int(task["revision"]) != request.expected_revision
or int(task["attempt"]) != request.expected_attempt
):
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_PROJECTION_CAS_MISMATCH", 409
)
if (
task["claimed_by"] not in (None, "")
or task["lease_token_hash"] not in (None, "")
or task["lease_expires_at"] not in (None, "")
):
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_ACTIVE_LEASE_DENY", 409
)
metadata = self._json_value(task["metadata_json"], "TASK_METADATA_INVALID")
capabilities = self._json_value(
task["required_capabilities_json"], "TASK_CAPABILITIES_INVALID"
)
result = self._json_value(task["result_json"], "PLANNER_RESULT_INVALID")
verification = self._json_value(
task["verification_json"], "PLANNER_VERIFICATION_INVALID"
)
if (
not isinstance(metadata, Mapping)
or not isinstance(capabilities, list)
or not isinstance(result, Mapping)
or not isinstance(verification, Mapping)
):
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_STATE_INVALID", 409
)
metadata, result, verification = dict(metadata), dict(result), dict(verification)
if _sha(metadata) != request.expected_metadata_sha256:
raise InitialExecutionProjectionError("TASK_METADATA_DRIFT", 409)
if _sha(capabilities) != request.expected_required_capabilities_sha256:
raise InitialExecutionProjectionError("TASK_CAPABILITIES_DRIFT", 409)
if _sha(result) != request.expected_planner_result_sha256:
raise InitialExecutionProjectionError("PLANNER_RESULT_DRIFT", 409)
if _sha(verification) != request.previous_verification_sha256:
raise InitialExecutionProjectionError("PLANNER_VERIFICATION_DRIFT", 409)
old = metadata.get("execution")
if not isinstance(old, Mapping):
raise InitialExecutionProjectionError("TASK_EXECUTION_METADATA_MISSING", 409)
old_execution = validate_execution_envelope(dict(old))
if (
old_execution.get("action") != "agent_runtime"
or old_execution.get("adapter") != "openclaw"
or old_execution.get("capability_scope") != [RUNTIME_CAPABILITY]
or old_execution.get("execution_digest")
!= request.expected_old_execution_digest
or capabilities != [RUNTIME_CAPABILITY]
):
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_EXECUTION_CONTRACT_INVALID", 409
)
expected_result = {
"task_id": task_id,
"job_id": request.expected_job_id,
"status": "SUCCEEDED",
"execution_mode": "agent_runtime",
"agent_runtime": "openclaw",
"execution_digest": request.expected_old_execution_digest,
"control_plane_access_granted_to_runtime": False,
"credential_material_exposed": False,
"secret_material_exposed": False,
"changed_files": [],
"writer_lease": "NONE",
"direct_main_write": False,
"production_write": False,
"force_push": False,
"protected_ref_bypass": False,
}
if any(result.get(name) != expected for name, expected in expected_result.items()):
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_RESULT_CONTRACT_INVALID", 409
)
if verification.get("decision") != "NEEDS_REWORK":
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_NEEDS_REWORK_REQUIRED", 409
)
self._planner_event_chain(connection, task_id)
return task, metadata, result
def project(
self,
task_id: str,
request: SupervisorCoordinatorProjectionRequest,
) -> dict[str, Any]:
task_id = _require_string(task_id, "TASK_ID_INVALID", TASK_RE)
if request.authorization_id != _required_authorization_id():
raise InitialExecutionProjectionError("AUTHORIZATION_ID_MISMATCH", 403)
request_sha = _sha(request.public())
with self._connect() as connection:
self._ensure_table(connection)
replay = connection.execute(
"SELECT * FROM task_supervisor_coordinator_projections "
"WHERE task_id=? AND idempotency_key=?",
(task_id, request.idempotency_key),
).fetchone()
if replay is not None:
if replay["request_sha256"] != request_sha:
raise InitialExecutionProjectionError("IDEMPOTENCY_CONFLICT", 409)
return {
"task_id": task_id,
"status": "QUEUED",
"attempt": replay["attempt"],
"revision": replay["applied_revision"],
"execution_digest": replay["new_execution_digest"],
"required_capabilities": COORDINATOR_CAPABILITIES,
"idempotent": True,
}
with self._connect() as connection:
task, _, result = self._load(connection, task_id, request)
runtime_input = self._runtime_input(task)
resolved = dict(
self.coordinator_resolver(task_id, request.expected_job_id, runtime_input)
)
binding = GovernedExecutionBinding.from_mapping(resolved)
if binding.task_id != task_id or binding.job_id != request.expected_job_id:
raise InitialExecutionProjectionError("GOVERNED_BINDING_CONFLICT", 409)
result_binding = {
name: result.get(name)
for name in (
"task_id",
"job_id",
"repository",
"base_oid",
"expected_branch_oid",
"target_branch",
"generation",
)
}
expected_binding = {
"task_id": binding.task_id,
"job_id": binding.job_id,
"repository": binding.repository,
"base_oid": binding.base_oid,
"expected_branch_oid": binding.expected_branch_oid,
"target_branch": binding.target_branch,
"generation": binding.generation,
}
if result_binding != expected_binding:
raise InitialExecutionProjectionError(
"SUPERVISOR_PLANNER_BINDING_CONFLICT", 409
)
execution = validate_execution_envelope(resolved, binding=binding)
if (
execution.get("action") != "agent_runtime"
or execution.get("adapter") != "openclaw"
or execution.get("capability_scope") != COORDINATOR_CAPABILITIES
or execution.get("change_set") != []
or execution.get("verification") != []
or execution.get("governance", {}).get("writer_lease") is not False
or execution.get("execution_digest") != request.expected_new_execution_digest
):
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_ENVELOPE_INVALID", 409
)
with self._connect() as connection:
connection.execute("BEGIN IMMEDIATE")
self._ensure_table(connection)
task, metadata, _ = self._load(connection, task_id, request)
new_metadata = dict(metadata)
new_metadata["execution"] = execution
new_metadata["execution_projection"] = {
"schema": "aios.online-agent.task-projection.v1",
"task_id": task_id,
"job_id": request.expected_job_id,
"execution_digest": execution["execution_digest"],
}
applied_revision = request.expected_revision + 1
cursor = connection.execute(
"""
UPDATE tasks SET metadata_json=?,required_capabilities_json=?,
revision=?,updated_at=?
WHERE task_id=? AND status=? AND revision=? AND attempt=?
AND claimed_by IS NULL AND lease_token_hash IS NULL
AND lease_expires_at IS NULL AND metadata_json=?
AND required_capabilities_json=? AND result_json=?
AND verification_json=?
""",
(
json.dumps(new_metadata, sort_keys=True, separators=(",", ":")),
json.dumps(COORDINATOR_CAPABILITIES, separators=(",", ":")),
applied_revision,
_now(),
task_id,
request.expected_status,
request.expected_revision,
request.expected_attempt,
task["metadata_json"],
task["required_capabilities_json"],
task["result_json"],
task["verification_json"],
),
)
if cursor.rowcount != 1:
raise InitialExecutionProjectionError(
"SUPERVISOR_COORDINATOR_PROJECTION_CAS_MISMATCH", 409
)
event = {
"schema": "aios.supervisor-coordinator-projection.event.v1",
"task_id": task_id,
"job_id": request.expected_job_id,
"authorization_id": request.authorization_id,
"idempotency_key": request.idempotency_key,
"reason": request.reason,
"old_execution_digest": request.expected_old_execution_digest,
"new_execution_digest": execution["execution_digest"],
"required_capabilities": COORDINATOR_CAPABILITIES,
"previous_revision": request.expected_revision,
"applied_revision": applied_revision,
"attempt": request.expected_attempt,
"request_sha256": request_sha,
"credential_forwarded_to_planner": False,
"writer_lease": False,
"main_write": False,
"production_write": False,
}
table, columns = self._event_table(connection)
values = {
"task_id": task_id,
"event_type": "TASK_SUPERVISOR_COORDINATOR_PROJECTION_AUTHORIZED",
"payload_json": json.dumps(event, sort_keys=True, separators=(",", ":")),
"created_at": _now(),
"agent_id": "owner-control",
}
names = [name for name in values if name in columns]
connection.execute(
f'INSERT INTO "{table}" ({",".join(chr(34)+name+chr(34) for name in names)}) '
f'VALUES ({",".join("?" for _ in names)})',
tuple(values[name] for name in names),
)
connection.execute(
"""
INSERT INTO task_supervisor_coordinator_projections(
task_id,idempotency_key,request_sha256,authorization_id,
expected_job_id,planner_result_sha256,old_execution_digest,
new_execution_digest,expected_revision,applied_revision,
attempt,created_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
task_id,
request.idempotency_key,
request_sha,
request.authorization_id,
request.expected_job_id,
request.expected_planner_result_sha256,
request.expected_old_execution_digest,
execution["execution_digest"],
request.expected_revision,
applied_revision,
request.expected_attempt,
_now(),
),
)
connection.commit()
return {
"task_id": task_id,
"status": "QUEUED",
"attempt": request.expected_attempt,
"revision": applied_revision,
"execution_digest": execution["execution_digest"],
"required_capabilities": COORDINATOR_CAPABILITIES,
"credential_forwarded_to_planner": False,
"idempotent": False,
}
class Handler(BaseHTTPRequestHandler):
store: SupervisorCoordinatorProjectionStore | None = None
control_token: str = ""
def log_message(self, *_args: Any) -> None:
return
def send_json(self, value: Mapping[str, Any], status: int) -> None:
raw = _canonical(dict(value))
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def do_GET(self) -> None:
if self.path == "/health":
self.send_json(
{"status": "ok", "service": "aios-supervisor-coordinator-projection"},
200,
)
else:
self.send_json({"error": "NOT_FOUND"}, 404)
def do_POST(self) -> None:
match = re.fullmatch(
r"/v1/tasks/([^/]+)/supervisor-coordinator-projection", self.path
)
header = self.headers.get("Authorization", "")
if match is None:
self.send_json({"error": "NOT_FOUND"}, 404)
return
if (
not header.startswith("Bearer ")
or not hmac.compare_digest(header[7:], self.control_token)
):
self.send_json({"error": "UNAUTHORIZED"}, 401)
return
try:
length = int(self.headers.get("Content-Length", "0"))
if length < 1 or length > MAX_BODY:
raise InitialExecutionProjectionError("REQUEST_SIZE_INVALID", 413)
value = json.loads(self.rfile.read(length).decode("utf-8"))
if not isinstance(value, Mapping) or self.store is None:
raise InitialExecutionProjectionError("REQUEST_INVALID", 400)
request = SupervisorCoordinatorProjectionRequest.from_mapping(value)
result = self.store.project(match.group(1), request)
self.send_json(result, 200 if result["idempotent"] else 201)
except InitialExecutionProjectionError as exc:
self.send_json({"error": exc.code}, exc.status)
except (ValueError, UnicodeDecodeError, json.JSONDecodeError):
self.send_json({"error": "REQUEST_INVALID"}, 400)
def serve() -> None:
credential = Path(
os.environ.get(
"AIOS_AGENT_TASK_CONTROL_TOKEN_FILE",
str(
Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
/ "agent_task_control_token"
),
)
)
token = load_control_token(credential)
bridge = os.environ.get("AIOS_GOVERNED_WRITE_API_URL", "http://127.0.0.1:18892")
db = Path(os.environ.get("AIOS_AGENT_TASK_DB", "/var/lib/aios-agent-task/state.sqlite3"))
Handler.control_token = token
Handler.store = SupervisorCoordinatorProjectionStore(
db, CanonicalCoordinatorExecutionResolver(bridge, token)
)
host = os.environ.get("AIOS_SUPERVISOR_COORDINATOR_PROJECTION_HOST", "127.0.0.1")
port = int(os.environ.get("AIOS_SUPERVISOR_COORDINATOR_PROJECTION_PORT", "18944"))
ThreadingHTTPServer((host, port), Handler).serve_forever()
if __name__ == "__main__":
serve()
@@ -0,0 +1,142 @@
"""Private Unix-socket caller projection and rehydration client.
The broker response body is deliberately public metadata only. The short-lived
credential projection is delivered in a private response header, consumed
inside the model API process, and never returned by an HTTP API or persisted.
"""
from __future__ import annotations
from dataclasses import dataclass
import hashlib
import http.client
import json
from pathlib import Path
import socket
from typing import Any, Mapping
CALLER = "chatgpt-governed-implementer-singlehost"
SCOPE = "release:candidate"
GOVERNED_TASK_SCOPE = "governed-task:project"
PROJECTED_SCOPES = frozenset({GOVERNED_TASK_SCOPE, SCOPE})
CREDENTIAL_HEADER = "X-AIOS-Caller-Projection-Credential"
class CallerProjectionError(RuntimeError):
def __init__(self, code: str, status: int = 503) -> None:
super().__init__(code)
self.code = code
self.status = status
@dataclass(frozen=True)
class ProjectionResult:
public: dict[str, Any]
credential_loaded: bool
class _UnixHTTPConnection(http.client.HTTPConnection):
def __init__(self, socket_path: Path, timeout: float) -> None:
super().__init__("localhost", timeout=timeout)
self.socket_path = socket_path
def connect(self) -> None:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(self.timeout)
try:
sock.connect(str(self.socket_path))
except Exception:
sock.close()
raise
self.sock = sock
class CallerProjectionClient:
def __init__(self, socket_path: Path, *, timeout: float = 2.0) -> None:
self.socket_path = Path(socket_path)
self.timeout = timeout
@staticmethod
def _idempotency_key(caller: str, scope: str) -> str:
digest = hashlib.sha256((caller + "\0" + scope).encode()).hexdigest()
return "caller-projection-" + digest[:32] + "-v1"
def rehydrate(
self,
caller: str,
scope: str,
request: Mapping[str, Any] | None = None,
) -> ProjectionResult:
if caller != CALLER:
raise CallerProjectionError("CALLER_MISMATCH", 403)
if scope not in PROJECTED_SCOPES:
raise CallerProjectionError("SCOPE_MISMATCH", 403)
body = {
"caller": caller,
"scope": scope,
"idempotency_key": self._idempotency_key(caller, scope),
"request": dict(request or {}),
}
connection = _UnixHTTPConnection(self.socket_path, self.timeout)
try:
connection.request(
"POST",
"/v1/project-caller",
body=json.dumps(body, sort_keys=True, separators=(",", ":")).encode(),
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"X-AIOS-Account": caller,
},
)
response = connection.getresponse()
status = response.status
raw = response.read()
credential = response.getheader(CREDENTIAL_HEADER, "")
except (OSError, TimeoutError, http.client.HTTPException) as exc:
raise CallerProjectionError("PROJECTION_BROKER_UNAVAILABLE", 503) from exc
finally:
connection.close()
try:
value = json.loads(raw.decode("utf-8"))
except Exception as exc:
raise CallerProjectionError("PROJECTION_RESPONSE_INVALID", 503) from exc
if not isinstance(value, Mapping):
raise CallerProjectionError("PROJECTION_RESPONSE_INVALID", 503)
if status != 200:
code = str(value.get("error") or "PROJECTION_DENIED")
raise CallerProjectionError(code, 403 if status < 500 else 503)
if any(
str(key).lower() in {
"credential",
"credential_material",
"token",
"bearer",
"secret",
"password",
}
for key in value
):
raise CallerProjectionError("PROJECTION_SECRET_FIELD_REJECTED", 503)
if value.get("caller") != CALLER or value.get("scope") != scope:
raise CallerProjectionError("PROJECTION_BINDING_INVALID", 503)
if value.get("projection_state") != "ACKED":
raise CallerProjectionError("PROJECTION_STATE_INVALID", 503)
if value.get("credential_state") != "REHYDRATED":
raise CallerProjectionError("CREDENTIAL_REHYDRATION_INVALID", 503)
if value.get("rehydration_state") not in {"GENERATED", "RECOVERED"}:
raise CallerProjectionError("REHYDRATION_STATE_INVALID", 503)
effective_scopes = value.get("effective_scopes")
if (
not isinstance(effective_scopes, list)
or len(effective_scopes) != len(set(effective_scopes))
or not all(isinstance(item, str) for item in effective_scopes)
or scope not in effective_scopes
or not set(effective_scopes).issubset(PROJECTED_SCOPES)
):
raise CallerProjectionError("PROJECTION_SCOPE_INVALID", 503)
if value.get("secret_exposed") is not False:
raise CallerProjectionError("PROJECTION_SECRET_EXPOSURE", 503)
if not isinstance(credential, str) or len(credential) < 32:
raise CallerProjectionError("CREDENTIAL_REHYDRATION_MISSING", 503)
return ProjectionResult(public=dict(value), credential_loaded=True)
@@ -39,6 +39,7 @@ _TASK_ALIASES: dict[str, frozenset[str]] = {
"gpu.train": frozenset({"legacy.gpu.train"}),
"agent.runtime.hermes": frozenset({"agent.runtime.hermes"}),
"agent.runtime.openclaw": frozenset({"agent.runtime.openclaw"}),
"engineering.autonomous": frozenset({"engineering.autonomous"}),
"release:candidate": frozenset({"release:candidate"}),
}
@@ -74,6 +75,7 @@ _WORKER_ALIASES: dict[str, frozenset[str]] = {
"gpu.train": frozenset({"legacy.gpu.train"}),
"agent.runtime.hermes": frozenset({"agent.runtime.hermes"}),
"agent.runtime.openclaw": frozenset({"agent.runtime.openclaw"}),
"engineering.autonomous": frozenset({"engineering.autonomous"}),
"release:candidate": frozenset({"release:candidate"}),
}
@@ -154,6 +156,11 @@ _CAPABILITY_CONTRACTS: dict[str, CapabilityContract] = {
dependencies=("openclaw_runtime",),
health_requirements=("worker_liveness",),
),
"engineering.autonomous": CapabilityContract(
"engineering.autonomous",
dependencies=("agent_task_control_plane",),
health_requirements=("worker_liveness",),
),
"release:candidate": CapabilityContract(
"release:candidate",
min_version=1,
@@ -1,9 +1,10 @@
"""Loopback-only governed Bridge v2 HTTP gateway with durable handlers."""
from __future__ import annotations
from dataclasses import dataclass, replace
from dataclasses import asdict, dataclass, replace
from datetime import datetime, timedelta, timezone
import base64
import hmac
import json
import os
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
@@ -12,6 +13,8 @@ import re
import sqlite3
import threading
from typing import Any, Mapping
import urllib.error
import urllib.request
from aios_authority_git_executor import AuthorityGitExecutor
from aios_chatgpt_bridge_v2 import BridgeV2Adapter, BridgeV2Error, OPERATION_REQUIRED_FIELDS
@@ -19,7 +22,9 @@ from aios_continuation_executor import AuthorityGitAdapter, ContinuationCommit,
from aios_continuation_lease import WriterLeaseRequestV2
from aios_control_plane_snapshot import build_control_plane_snapshot
from aios_gitops_job_controller import GitOpsJobController, JobError, JobState, JobStore
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBootstrap
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBinding, GovernedTaskBootstrap, _legacy_safe, _task_key
from aios_online_agent_execution import ExecutionProjectionError, OnlineTaskProjectionClient
from aios_online_agent_plan_store import ExecutionPlanError, ExecutionPlanStore
from governed_production_release import build_executor_config
MAX_BODY = 2 * 1024 * 1024
@@ -27,6 +32,8 @@ ROUTES = {
"/v2/runtime": "getRuntimeStatus",
"/v2/capabilities": "getGovernedWriteCapabilities",
"/v2/governed-task": "createGovernedTask",
"/v2/execution-envelope": "projectOnlineAgentExecution",
"/v2/execution-plan": "registerOnlineAgentExecutionPlan",
"/v2/writer-lease": "acquireWriterLease",
"/v2/writer-lease/bootstrap": "bootstrapWriterLease",
"/v2/writer-lease/renew": "renewWriterLease",
@@ -62,6 +69,9 @@ class RuntimeConfig:
registry_root: Path = Path("/var/lib/aios-gitops/runtime/registry-projection")
baseline_path: Path = Path("/var/lib/aios-gitops-runtime-anchor/baseline/current.json")
health_url: str = "http://127.0.0.1:18927/health"
agent_task_api_url: str = "http://127.0.0.1:18940"
agent_task_control_token_path: Path = Path("/run/credentials/aios-chatgpt-bridge-v2-staging.service/agent_task_control_token")
online_task_projection_enabled: bool = False
def _safe(value: str) -> str:
@@ -74,6 +84,16 @@ class GovernedRuntime:
config.state_root.mkdir(parents=True, exist_ok=True)
self.authority = AuthorityGitExecutor(config.authoritative_git_dir, config.state_root / "worktrees")
self.bootstrap = GovernedTaskBootstrap(self.authority, config.state_root, remote_name=config.remote_name, token_path=config.token_path, ssh_key_path=config.ssh_key_path, api_base=config.api_base)
self.online_task_projector = (
OnlineTaskProjectionClient(config.agent_task_api_url, config.agent_task_control_token_path)
if config.online_task_projection_enabled
else None
)
self.projection_outbox_root = config.state_root / "online-task-projection-outbox"
self.projection_outbox_root.mkdir(parents=True, exist_ok=True)
self.execution_plan_store = ExecutionPlanStore(config.state_root / "online-agent-execution-plans")
self._projection_locks_guard = threading.Lock()
self._projection_locks: dict[str, threading.RLock] = {}
self.runtimes: dict[tuple[str, int], tuple[Any, GitOpsJobController, ContinuationExecutor]] = {}
self.controllers: dict[str, GitOpsJobController] = {}
self._recovery_lock = threading.RLock()
@@ -93,6 +113,54 @@ class GovernedRuntime:
askpass.write_text('#!/bin/sh\ncase "$1" in *Username*) printf "%s\\n" "$GITEA_MACHINE_ACTOR" ;; *) cat "$CREDENTIAL_FILE" ;; esac\n', encoding="utf-8")
askpass.chmod(0o700)
os.environ.setdefault("GIT_ASKPASS", str(askpass)); os.environ.setdefault("GIT_ASKPASS_REQUIRE", "force"); os.environ.setdefault("GIT_TERMINAL_PROMPT", "0"); os.environ.setdefault("GITEA_MACHINE_ACTOR", "aios-autonomy-agent")
if self.online_task_projector is not None:
threading.Thread(target=self._projection_reconciler, name="online-task-projection-reconciler", daemon=True).start()
def _projection_lock(self, task_id: str) -> threading.RLock:
guard = getattr(self, "_projection_locks_guard", None)
if guard is None:
guard = threading.Lock()
self._projection_locks_guard = guard
self._projection_locks = {}
with guard:
return self._projection_locks.setdefault(task_id, threading.RLock())
def _projection_outbox_path(self, task_id: str) -> Path:
return self.projection_outbox_root / (_task_key(task_id) + ".json")
def _write_projection_outbox(self, task_id: str, projection: Mapping[str, Any], binding: Any) -> Path:
path = self._projection_outbox_path(task_id)
temporary = path.with_name(path.name + f".{os.getpid()}.{threading.get_ident()}.tmp")
temporary.write_text(json.dumps({"task": dict(projection), "binding": asdict(binding)}, sort_keys=True, separators=(",", ":")), encoding="utf-8")
temporary.replace(path)
return path
def _deliver_projection_outbox(self, path: Path) -> dict[str, Any]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, Mapping) or not isinstance(value.get("task"), Mapping) or not isinstance(value.get("binding"), Mapping):
raise RuntimeError("ONLINE_TASK_PROJECTION_OUTBOX_INVALID")
binding = self.bootstrap.execution_binding(GovernedTaskBinding(**dict(value["binding"])))
if self.online_task_projector is None:
raise RuntimeError("ONLINE_TASK_PROJECTION_CONTROL_UNAVAILABLE")
created = self.online_task_projector.create(dict(value["task"]), binding=binding)
path.unlink(missing_ok=True)
return created
def _projection_reconciler(self) -> None:
while True:
for path in sorted(self.projection_outbox_root.glob("*.json")):
try:
value = json.loads(path.read_text(encoding="utf-8"))
binding = value.get("binding") if isinstance(value, Mapping) else None
task_id = str(binding.get("task_id") or "") if isinstance(binding, Mapping) else ""
if not task_id:
continue
with self._projection_lock(task_id):
if path.exists():
self._deliver_projection_outbox(path)
except Exception:
continue
threading.Event().wait(5)
def runtime_status(self) -> dict[str, Any]:
return {
@@ -113,7 +181,10 @@ class GovernedRuntime:
}
def _job_root(self, task_id: str) -> Path:
return self.config.state_root / "jobs" / _safe(task_id)
parent = self.config.state_root / "jobs"
keyed = parent / _task_key(task_id)
legacy = parent / _legacy_safe(task_id)
return keyed if keyed.exists() or not legacy.exists() else legacy
def _build_runtime(
self,
@@ -443,7 +514,85 @@ class GovernedRuntime:
return result
def create_task(self, request: Mapping[str, Any]) -> dict[str, Any]:
result = self.bootstrap.create(request); self.bindings[result["task_id"]] = result; return result
online_request = request.get("online_task")
if online_request is not None and not isinstance(online_request, Mapping):
raise ValueError("ONLINE_TASK_REQUEST_INVALID")
execution_request = request.get("execution")
if execution_request is not None and not isinstance(execution_request, Mapping):
raise ValueError("EXECUTION_REQUEST_INVALID")
online_task_projector = getattr(self, "online_task_projector", None)
projection_required = (
online_request is not None
or execution_request is not None
or request.get("require_online_task_projection") is True
)
if online_task_projector is None:
if projection_required:
raise RuntimeError("ONLINE_TASK_PROJECTION_CONTROL_UNAVAILABLE")
return self.bootstrap.create(request)
online = dict(online_request or {})
online.setdefault("goal", str(request.get("description") or "Governed engineering task"))
online.setdefault("instructions", "Execute only the exact governed binding and return server-verifiable evidence.")
online.setdefault("success_criteria", [{"id": "governed", "description": "Complete the exact governed task lifecycle."}])
online.setdefault("required_capabilities", ["git.read", "git.test", "governed.write.branch", "governed.write.file"])
online.setdefault("priority", int(request.get("priority") or 0))
online.setdefault("max_attempts", int(request.get("max_attempts") or 10))
normalized = dict(request)
normalized["online_task"] = online
normalized["execution"] = dict(execution_request or {})
normalized["require_online_task_projection"] = True
result = self.bootstrap.create(normalized)
self.bindings[result["task_id"]] = result
projection = self.bootstrap.online_task_projection(
result["task_id"],
online,
normalized["execution"],
expected_job_id=str(result.get("job_id") or "") or None,
)
durable_binding = self.bootstrap.get_binding(result["task_id"])
with self._projection_lock(result["task_id"]):
path = self._write_projection_outbox(result["task_id"], projection, durable_binding)
created = self._deliver_projection_outbox(path)
return {
**result,
"online_task_projection": {
"status": "PROJECTED",
"task_id": created["task_id"],
"job_id": result["job_id"],
"task_status": created["status"],
"execution_digest": projection["metadata"]["execution"]["execution_digest"],
},
}
def project_online_agent_execution(self, request: Mapping[str, Any]) -> dict[str, Any]:
capabilities = request.get("required_capabilities") or []
if not isinstance(capabilities, list) or not all(isinstance(item, str) for item in capabilities):
raise ValueError("CAPABILITY_SCOPE_INVALID")
execution = request.get("execution")
if execution is not None and not isinstance(execution, Mapping):
raise ValueError("EXECUTION_REQUEST_INVALID")
task_id = str(request["task_id"])
durable = self.bootstrap.get_binding(task_id)
binding = self.bootstrap.execution_binding(durable)
expected_job_id = str(request.get("job_id") or "") or None
if expected_job_id and expected_job_id != binding.job_id:
raise BootstrapError("JOB_BINDING_CONFLICT")
if execution is None:
planned = self.execution_plan_store.resolve(binding)
if planned is not None:
execution = planned["execution"]
return self.bootstrap.execution_envelope(
task_id,
execution,
required_capabilities=tuple(capabilities),
expected_job_id=expected_job_id,
)
def register_online_agent_execution_plan(self, request: Mapping[str, Any]) -> dict[str, Any]:
task_id = str(request["task_id"])
durable = self.bootstrap.get_binding(task_id)
binding = self.bootstrap.execution_binding(durable)
return self.execution_plan_store.register(binding, request)
def acquire_lease(self, request: Mapping[str, Any]) -> dict[str, Any]:
binding, controller, _ = self._runtime(request)
@@ -1340,7 +1489,19 @@ class GovernedRuntime:
controller = self._controller_for_job(job_id)
row = controller.store.read(job_id)
if action == "get":
return controller.store._receipt(row, duplicate=True).as_dict()
response = controller.store._receipt(row, duplicate=True).as_dict()
try:
with controller.store._connect() as connection:
connection.row_factory = sqlite3.Row
leases = connection.execute(
"SELECT lease_id,job_id,branch,lineage_anchor_oid,current_head_oid,generation,"
"lease_expires_at,state,revision FROM branch_leases WHERE job_id=? ORDER BY revision DESC",
(job_id,),
).fetchall()
except sqlite3.Error as exc:
raise RuntimeError("JOB_DB_UNAVAILABLE") from exc
response["branch_leases"] = [dict(item) for item in leases]
return response
if action == "resume":
return controller.resume(job_id).as_dict()
return self._executor_from_row(row, controller).reconcile(job_id)
@@ -1405,6 +1566,8 @@ class GovernedRuntime:
def handlers(self) -> dict[str, Any]:
return {
"createGovernedTask": self.create_task,
"projectOnlineAgentExecution": self.project_online_agent_execution,
"registerOnlineAgentExecutionPlan": self.register_online_agent_execution_plan,
"acquireWriterLease": self.acquire_lease,
"bootstrapWriterLease": self.bootstrap_writer_lease,
"renewWriterLease": self.renew_writer_lease,
@@ -1434,6 +1597,8 @@ class _HTTPError(RuntimeError): pass
class BridgeHandler(BaseHTTPRequestHandler):
adapter: BridgeV2Adapter | None = None
control_token: str | None = None
agent_task_api_url: str = "http://127.0.0.1:18940"
def log_message(self, *_args: Any) -> None: return
def _json(self, value: Any, status: int = 200) -> None:
raw = json.dumps(value, sort_keys=True, separators=(",", ":")).encode(); self.send_response(status); self.send_header("Content-Type", "application/json"); self.send_header("Content-Length", str(len(raw))); self.end_headers(); self.wfile.write(raw)
@@ -1451,7 +1616,50 @@ class BridgeHandler(BaseHTTPRequestHandler):
allowed = set(OPERATION_REQUIRED_FIELDS[operation]) | {"schema", "job_id", "lease_seconds", "create_target_branch", "worktree_mode", "cas_ref_update_required", "expected_branch_oid", "generation", "branch_mode"}
if operation == "commitFilesGoverned": allowed.add("files")
allowed.update({"owner_approval", "execute"})
if operation == "projectOnlineAgentExecution":
allowed.update({"execution", "required_capabilities"})
if operation == "registerOnlineAgentExecutionPlan":
allowed.update({"execution", "source_head_oid", "expected_previous_plan_digest"})
if operation == "createGovernedTask":
allowed.update({"online_task", "execution", "require_online_task_projection", "priority", "max_attempts", "parent_task_id", "parent_job_id"})
if set(body) - allowed: raise _HTTPError("UNKNOWN_FIELDS")
def _authorized(self, operation: str, body: Mapping[str, Any]) -> bool:
header = self.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return False
supplied = header[7:]
if self.control_token and hmac.compare_digest(supplied, self.control_token):
return True
poll = urllib.request.Request(
self.agent_task_api_url.rstrip("/") + "/v1/tasks/poll",
method="POST",
data=b'{"limit":1}',
headers={"Authorization": header, "Content-Type": "application/json", "Accept": "application/json"},
)
try:
with urllib.request.urlopen(poll, timeout=5) as response:
principal = json.loads(response.read().decode("utf-8"))
except (OSError, urllib.error.URLError, urllib.error.HTTPError, ValueError):
return False
agent_id = str(principal.get("agent_id") or "") if isinstance(principal, Mapping) else ""
if not agent_id:
return False
claim_bound = {"acquireWriterLease", "bootstrapWriterLease", "renewWriterLease", "commitFileGoverned", "commitFilesGoverned", "projectOnlineAgentExecution", "getGitOpsJob"}
if operation not in claim_bound:
return False
task_id = str(body.get("task_id") or "")
if not task_id:
return False
task_request = urllib.request.Request(
self.agent_task_api_url.rstrip("/") + "/v1/tasks/" + task_id,
headers={"Authorization": header, "Accept": "application/json"},
)
try:
with urllib.request.urlopen(task_request, timeout=5) as response:
task = json.loads(response.read().decode("utf-8"))
except (OSError, urllib.error.URLError, urllib.error.HTTPError, ValueError):
return False
return isinstance(task, Mapping) and task.get("task_id") == task_id and task.get("claimed_by") == agent_id and task.get("status") in {"CLAIMED", "RUNNING"}
def do_GET(self) -> None:
if self.adapter is None: self._json({"error": "RUNTIME_UNAVAILABLE"}, 503); return
if self.path == "/v2/runtime": self._json(self.adapter.getRuntimeStatus()); return
@@ -1462,14 +1670,23 @@ class BridgeHandler(BaseHTTPRequestHandler):
operation = ROUTES.get(self.path)
if operation is None or operation in ("getRuntimeStatus", "getGovernedWriteCapabilities") or self.adapter is None: self._json({"error": "NOT_FOUND"}, 404); return
try:
body = self._body(); self._allowed(operation, body); self._json(getattr(self.adapter, operation)(body))
body = self._body()
except _HTTPError as exc: self._json({"error": str(exc)}, 413 if str(exc) == "REQUEST_TOO_LARGE" else 400); return
if not self._authorized(operation, body): self._json({"error": "BRIDGE_CALLER_UNAUTHORIZED"}, 401); return
try:
self._allowed(operation, body); self._json(getattr(self.adapter, operation)(body))
except _HTTPError as exc: self._json({"error": str(exc)}, 413 if str(exc) == "REQUEST_TOO_LARGE" else 400)
except (BootstrapError, ValueError, BridgeV2Error) as exc: self._json({"error": str(exc)}, 409 if "CONFLICT" in str(exc) else 400)
except (BootstrapError, ExecutionProjectionError, ExecutionPlanError, ValueError, BridgeV2Error) as exc: self._json({"error": str(exc)}, 409 if any(token in str(exc) for token in ("CONFLICT", "DRIFT", "CAS")) else 400)
except Exception as exc: self._json({"error": str(exc) or type(exc).__name__}, 409 if any(token in str(exc) for token in ("CONFLICT", "EXPECTATION", "MONOTONIC")) else 503)
def serve(adapter: BridgeV2Adapter, host: str = "127.0.0.1", port: int = 18892) -> None:
BridgeHandler.adapter = adapter; ThreadingHTTPServer((host, port), BridgeHandler).serve_forever()
def serve(adapter: BridgeV2Adapter, host: str = "127.0.0.1", port: int = 18892, *, control_token_path: Path, agent_task_api_url: str) -> None:
if not control_token_path.is_file() or control_token_path.is_symlink():
raise RuntimeError("BRIDGE_CONTROL_CREDENTIAL_UNAVAILABLE")
control_token = control_token_path.read_text(encoding="utf-8").strip()
if len(control_token) < 32:
raise RuntimeError("BRIDGE_CONTROL_CREDENTIAL_INVALID")
BridgeHandler.adapter = adapter; BridgeHandler.control_token = control_token; BridgeHandler.agent_task_api_url = agent_task_api_url; ThreadingHTTPServer((host, port), BridgeHandler).serve_forever()
def main() -> None:
@@ -1488,11 +1705,21 @@ def main() -> None:
remote_name=os.environ.get("AIOS_GITEA_REMOTE", "gitea-http"),
api_base=os.environ.get("AIOS_GITEA_API_BASE", "https://g.cnnetmkt.top/git/api/v1"),
target_version=os.environ.get("AIOS_CONTROL_PLANE_TARGET_VERSION", "V2R2C1"),
agent_task_api_url=os.environ.get("AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940"),
agent_task_control_token_path=Path(
os.environ.get(
"AIOS_AGENT_TASK_CONTROL_TOKEN_FILE",
str(Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials/aios-chatgpt-bridge-v2-staging.service")) / "agent_task_control_token"),
)
),
online_task_projection_enabled=os.environ.get("AIOS_ONLINE_TASK_PROJECTION_ENABLED", "1") == "1",
)
runtime = GovernedRuntime(config)
serve(
BridgeV2Adapter(runtime_status=runtime.runtime_status(), handlers=runtime.handlers()),
port=int(os.environ.get("AIOS_BRIDGE_V2_PORT", "18892")),
control_token_path=config.agent_task_control_token_path,
agent_task_api_url=config.agent_task_api_url,
)
@@ -157,7 +157,7 @@ class DetachedGitWorkspace:
def materialize_files(self, files: Mapping[str, bytes]) -> None:
dirty = self._dirty_paths()
expected_paths = set(files)
if dirty and dirty != expected_paths:
if dirty and not dirty.issubset(expected_paths):
raise ContinuationExecutionError("WORKTREE_DIRTY_SCOPE_MISMATCH")
for relative, content in files.items():
target = (self.path / relative).resolve()
@@ -11,19 +11,24 @@ or runtime switching.
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
import argparse
import hashlib
import grp
import hmac
from http.server import BaseHTTPRequestHandler
import json
import os
import pwd
from pathlib import Path
import re
import secrets
import sqlite3
import socket
import socketserver
import stat
import tempfile
import struct
import threading
import urllib.error
import urllib.request
@@ -34,6 +39,14 @@ RECEIPT_SCHEMA = "aios.owner-durable-authorization-receipt.v1"
DECISION_SCHEMA = "aios.owner-durable-authorization-decision.v1"
PROJECTION_SCHEMA = "aios.external-capability-projection.request.v1"
PROJECTION_LIST_SCHEMA = "aios.external-capability-projection.list.v1"
CALLER_PROJECTION_SCHEMA = "aios.caller-admission-projection.v1"
CALLER_PROJECTION_TTL_DEFAULT = 300
CALLER_PROJECTION_SCOPES = frozenset({
"governed-task:project",
"release:candidate",
})
GATE_AMENDMENT_SCHEMA = "aios.release-preparation.gate-amendment.v1"
GATE_AMENDMENT_CLASSIFICATION = "SUPERVISOR_GLOBAL_EXTERNAL_ACK_GATE_REMEDIATED"
ALLOWED_PERSISTENT_CAPABILITIES = frozenset({
"governed-task:project",
@@ -64,6 +77,80 @@ TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
JOB_RE = re.compile(r"^job-[0-9a-f]{32}$")
MAX_BODY = 512 * 1024
SO_PEERCRED_SIZE = struct.calcsize("3i")
def _peer_process_status(pid: int) -> tuple[int, int, set[int]]:
status_path = Path(f"/proc/{pid}/status")
try:
lines = status_path.read_text(encoding="utf-8").splitlines()
except (OSError, UnicodeError) as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
fields: dict[str, str] = {}
for line in lines:
if ":" in line:
key, value = line.split(":", 1)
if key in {"Uid", "Gid", "Groups"}:
fields[key] = value.strip()
try:
uid = int(fields["Uid"].split()[0])
gid = int(fields["Gid"].split()[0])
groups = {int(item) for item in fields["Groups"].split()}
except (KeyError, IndexError, ValueError) as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
return uid, gid, groups
def _peer_process_groups(pid: int) -> set[int]:
return _peer_process_status(pid)[2]
def peer_credentials_from_connection(connection: object) -> tuple[int, int, int, set[int]]:
if not hasattr(socket, "SO_PEERCRED"):
raise AuthorizationError("SO_PEERCRED_UNAVAILABLE", 500)
try:
raw = connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, SO_PEERCRED_SIZE)
except OSError as exc:
raise AuthorizationError("SO_PEERCRED_UNAVAILABLE", 403) from exc
if len(raw) != SO_PEERCRED_SIZE:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
try:
pid, uid, gid = struct.unpack("3i", raw)
except struct.error as exc:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403) from exc
if pid <= 1 or uid < 0 or gid < 0:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
process_uid, process_gid, groups = _peer_process_status(pid)
if process_uid != uid or process_gid != gid:
raise AuthorizationError("SOCKPEER_CREDENTIALS_INVALID", 403)
return pid, uid, gid, groups | {gid}
def peer_user_from_connection(connection: object) -> str:
_pid, uid, _gid, _groups = peer_credentials_from_connection(connection)
try:
return pwd.getpwuid(uid).pw_name
except (KeyError, OSError) as exc:
raise AuthorizationError("SO_PEERCRED_UID_UNKNOWN", 403) from exc
CANONICAL_BROKER_GROUP = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_GROUP",
"aios-autonomous-agent-worker",
).strip()
def authorize_broker_peer(connection: object) -> bool:
_pid, _uid, _gid, groups = peer_credentials_from_connection(connection)
if not CANONICAL_BROKER_GROUP:
raise AuthorizationError("SOCKGROUP_CONFIGURATION_INVALID", 503)
try:
canonical_gid = grp.getgrnam(CANONICAL_BROKER_GROUP).gr_gid
except KeyError as exc:
raise AuthorizationError("SOCKGROUP_CONFIGURATION_INVALID", 503) from exc
if canonical_gid not in groups:
raise AuthorizationError("SOCKGROUP_NOT_AUTHORIZED", 403)
return True
class AuthorizationError(RuntimeError):
@@ -134,6 +221,7 @@ class DurablePolicy:
persistent_until_revoked: bool
constraints: Constraints
policy_sha256: str
expires_at: datetime | None
@classmethod
def load(
@@ -158,10 +246,14 @@ class DurablePolicy:
"capabilities",
"issued_at",
"not_before",
"expires_at",
"persistent_until_revoked",
"constraints",
}
if set(value) != allowed_fields or value.get("schema") != POLICY_SCHEMA:
if (
not set(value).issubset(allowed_fields)
or value.get("schema") != POLICY_SCHEMA
):
raise AuthorizationError("POLICY_FIELDS_INVALID", 503)
grant_id = str(value.get("grant_id") or "")
@@ -190,6 +282,13 @@ class DurablePolicy:
current = now or datetime.now(timezone.utc)
if issued_at > not_before or current < not_before:
raise AuthorizationError("POLICY_NOT_ACTIVE", 403)
expires_at = None
if value.get("expires_at") is not None:
expires_at = parse_time(value.get("expires_at"), field="expires_at")
if expires_at <= not_before:
raise AuthorizationError("POLICY_EXPIRY_INVALID", 503)
if current >= expires_at:
raise AuthorizationError("POLICY_EXPIRED", 403)
if value.get("persistent_until_revoked") is not True:
raise AuthorizationError("POLICY_PERSISTENCE_INVALID", 503)
@@ -243,8 +342,43 @@ class DurablePolicy:
max_task_risk=max_risk,
),
policy_sha256=sha256(policy_path.read_bytes()),
expires_at=expires_at,
)
def validate_caller_scope(
self,
caller: str,
scope: str,
request: Mapping[str, Any],
) -> dict[str, Any]:
if caller != self.account:
raise AuthorizationError("CALLER_MISMATCH", 403)
if scope not in CALLER_PROJECTION_SCOPES:
raise AuthorizationError("SCOPE_MISMATCH", 403)
if scope not in self.capabilities:
raise AuthorizationError("CAPABILITY_NOT_AUTHORIZED", 403)
if not isinstance(request, Mapping):
raise AuthorizationError("CALLER_PROJECTION_REQUEST_INVALID")
if scope == "release:candidate":
if set(request) != {"source_commit", "version"}:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
source_commit = str(request.get("source_commit") or "")
version = str(request.get("version") or "")
if OID_RE.fullmatch(source_commit) is None:
raise AuthorizationError("RELEASE_SOURCE_COMMIT_INVALID")
if VERSION_RE.fullmatch(version) is None or not version.startswith(
self.constraints.release_version_prefix
):
raise AuthorizationError("RELEASE_VERSION_NOT_AUTHORIZED", 403)
elif request:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
return {
"schema": CALLER_PROJECTION_SCHEMA,
"caller": caller,
"scope": scope,
"request_sha256": sha256(canonical(request)),
}
def authorize(self, capability: str, request: Mapping[str, Any]) -> dict[str, Any]:
if capability not in self.capabilities:
raise AuthorizationError("CAPABILITY_NOT_AUTHORIZED", 403)
@@ -447,9 +581,160 @@ class ReceiptStore:
ON projection_outbox(state, capability, created_at)
"""
)
con.execute(
"""
CREATE TABLE IF NOT EXISTS caller_projections(
projection_id TEXT PRIMARY KEY,
caller TEXT NOT NULL,
scope TEXT NOT NULL,
grant_id TEXT NOT NULL,
policy_sha256 TEXT NOT NULL,
projection_state TEXT NOT NULL,
credential_state TEXT NOT NULL,
rehydration_state TEXT NOT NULL,
issued_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(caller, scope)
)
"""
)
con.execute(
"""
CREATE TABLE IF NOT EXISTS gate_amendments(
idempotency_key TEXT PRIMARY KEY,
amendment_sha256 TEXT NOT NULL,
amendment_json TEXT NOT NULL,
created_at TEXT NOT NULL
)
"""
)
con.commit()
self._lock = threading.RLock()
@staticmethod
def caller_projection_id(caller: str, scope: str) -> str:
digest = sha256(canonical({"caller": caller, "scope": scope}))
return "caller-projection-" + digest[:32]
def upsert_caller_projection(
self,
*,
caller: str,
scope: str,
grant_id: str,
policy_sha256: str,
ttl_seconds: int,
effective_scopes: Any,
) -> dict[str, Any]:
if not 30 <= ttl_seconds <= 900:
raise AuthorizationError("CALLER_PROJECTION_TTL_INVALID", 503)
normalized_scopes = sorted({str(item) for item in effective_scopes})
if (
not normalized_scopes
or scope not in normalized_scopes
or not set(normalized_scopes).issubset(CALLER_PROJECTION_SCOPES)
):
raise AuthorizationError("CALLER_PROJECTION_SCOPES_INVALID", 503)
requested_projection_id = self.caller_projection_id(caller, scope)
issued = datetime.now(timezone.utc)
issued_at = issued.isoformat()
expires_at = (issued + timedelta(seconds=ttl_seconds)).isoformat()
requested_recovery = False
with self._lock, sqlite3.connect(self.path) as con:
con.row_factory = sqlite3.Row
con.execute("BEGIN IMMEDIATE")
for projected_scope in normalized_scopes:
projection_id = self.caller_projection_id(caller, projected_scope)
row = con.execute(
"SELECT * FROM caller_projections WHERE caller=? AND scope=?",
(caller, projected_scope),
).fetchone()
if row is not None and row["projection_state"] not in {"ACKED", "PENDING"}:
con.rollback()
raise AuthorizationError("PROJECTION_STATE_INVALID", 503)
recovery = row is not None
if projected_scope == scope:
requested_recovery = recovery
if row is None:
con.execute(
"""
INSERT INTO caller_projections(
projection_id,caller,scope,grant_id,policy_sha256,
projection_state,credential_state,rehydration_state,
issued_at,expires_at,updated_at
) VALUES(?,?,?,?,?,?,?,?,?,?,?)
""",
(
projection_id,
caller,
projected_scope,
grant_id,
policy_sha256,
"PENDING",
"GENERATING",
"RECOVERING" if recovery else "GENERATING",
issued_at,
expires_at,
issued_at,
),
)
else:
con.execute(
"""
UPDATE caller_projections
SET projection_id=?,grant_id=?,policy_sha256=?,
projection_state='PENDING',
credential_state='GENERATING',
rehydration_state=?,
issued_at=?,expires_at=?,updated_at=?
WHERE caller=? AND scope=?
""",
(
projection_id,
grant_id,
policy_sha256,
"RECOVERING",
issued_at,
expires_at,
issued_at,
caller,
projected_scope,
),
)
con.execute(
"""
UPDATE caller_projections
SET projection_state='ACKED',
credential_state='REHYDRATED',
rehydration_state=?,
updated_at=?
WHERE caller=? AND scope=?
""",
(
"RECOVERED" if recovery else "GENERATED",
issued_at,
caller,
projected_scope,
),
)
con.commit()
return {
"schema": CALLER_PROJECTION_SCHEMA,
"projection_id": requested_projection_id,
"caller": caller,
"scope": scope,
"grant_id": grant_id,
"policy_sha256": policy_sha256,
"projection_state": "ACKED",
"credential_state": "REHYDRATED",
"rehydration_state": "RECOVERED" if requested_recovery else "GENERATED",
"effective_scopes": normalized_scopes,
"issued_at": issued_at,
"expires_at": expires_at,
"secret_exposed": False,
}
def get(self, key: str) -> dict[str, Any] | None:
with sqlite3.connect(self.path) as con:
row = con.execute(
@@ -519,6 +804,37 @@ class ReceiptStore:
)
con.commit()
def put_gate_amendment(
self,
idempotency_key: str,
amendment: Mapping[str, Any],
) -> dict[str, Any]:
encoded = json.dumps(amendment, sort_keys=True, separators=(",", ":"))
lowered = encoded.lower()
if any(marker in lowered for marker in (
"bearer ", "access_token", "refresh_token", "private_key", "credential_file"
)):
raise AuthorizationError("GATE_AMENDMENT_SECRET_MATERIAL_DENY", 403)
digest = sha256(encoded.encode())
with self._lock, sqlite3.connect(self.path) as con:
con.execute("BEGIN IMMEDIATE")
row = con.execute(
"SELECT amendment_sha256,amendment_json FROM gate_amendments WHERE idempotency_key=?",
(idempotency_key,),
).fetchone()
if row is not None:
if row[0] != digest or row[1] != encoded:
con.rollback()
raise AuthorizationError("GATE_AMENDMENT_IDEMPOTENCY_CONFLICT", 409)
con.commit()
return {"amendment_sha256": digest, "idempotent": True}
con.execute(
"INSERT INTO gate_amendments(idempotency_key,amendment_sha256,amendment_json,created_at) VALUES(?,?,?,?)",
(idempotency_key, digest, encoded, datetime.now(timezone.utc).isoformat()),
)
con.commit()
return {"amendment_sha256": digest, "idempotent": False}
@staticmethod
def projection_id(
grant_id: str,
@@ -716,12 +1032,83 @@ class Broker:
store: ReceiptStore,
upstream: Upstream,
expected_account: str,
expected_peer_user: str,
allowed_peer_users: frozenset[str] | None = None,
) -> None:
self.policy_path = policy_path
self.revocation_path = revocation_path
self.store = store
self.upstream = upstream
self.expected_account = expected_account
self.expected_peer_user = expected_peer_user
self.allowed_peer_users = allowed_peer_users or frozenset({expected_peer_user})
@staticmethod
def validate_caller_projection(
value: Mapping[str, Any],
*,
expected_scopes: Any = None,
) -> dict[str, Any]:
expected = sorted(
set(expected_scopes) if expected_scopes is not None else CALLER_PROJECTION_SCOPES
)
if value.get("caller") != "chatgpt-governed-implementer-singlehost":
raise AuthorizationError("CALLER_MISMATCH", 503)
if value.get("scope") not in expected:
raise AuthorizationError("SCOPE_MISMATCH", 503)
if value.get("projection_state") != "ACKED":
raise AuthorizationError("PROJECTION_STATE_INVALID", 503)
if value.get("credential_state") != "REHYDRATED":
raise AuthorizationError("CREDENTIAL_REHYDRATION_INVALID", 503)
if value.get("rehydration_state") not in {"GENERATED", "RECOVERED"}:
raise AuthorizationError("REHYDRATION_STATE_INVALID", 503)
if value.get("effective_scopes") != expected:
raise AuthorizationError("PROJECTION_SCOPE_INVALID", 503)
if value.get("secret_exposed") is not False:
raise AuthorizationError("PROJECTION_SECRET_EXPOSURE", 503)
return dict(value)
def project_caller(
self,
caller: str,
scope: str,
request: Mapping[str, Any],
*,
idempotency_key: str,
) -> dict[str, Any]:
if not GRANT_RE.fullmatch(idempotency_key):
raise AuthorizationError("IDEMPOTENCY_KEY_INVALID")
if caller != self.expected_account:
raise AuthorizationError("CALLER_MISMATCH", 403)
if scope not in CALLER_PROJECTION_SCOPES:
raise AuthorizationError("SCOPE_MISMATCH", 403)
policy = self.policy()
policy.validate_caller_scope(caller, scope, request)
effective_scopes = sorted(policy.capabilities & CALLER_PROJECTION_SCOPES)
try:
ttl_seconds = int(
os.environ.get(
"AIOS_OWNER_AUTH_CALLER_PROJECTION_TTL_SECONDS",
str(CALLER_PROJECTION_TTL_DEFAULT),
)
)
except ValueError as exc:
raise AuthorizationError("CALLER_PROJECTION_TTL_INVALID", 503) from exc
credential = secrets.token_urlsafe(32)
value = self.store.upsert_caller_projection(
caller=caller,
scope=scope,
grant_id=policy.grant_id,
policy_sha256=policy.policy_sha256,
ttl_seconds=ttl_seconds,
effective_scopes=effective_scopes,
)
value = self.validate_caller_projection(
value,
expected_scopes=effective_scopes,
)
value["_credential"] = credential
return value
def policy(self) -> DurablePolicy:
policy = DurablePolicy.load(self.policy_path, self.revocation_path)
@@ -758,24 +1145,90 @@ class Broker:
"separate_owner_release_approval_still_required": True,
}
@staticmethod
def validate_gate_amendment(
amendment: Mapping[str, Any],
*,
policy: DurablePolicy,
request: Mapping[str, Any],
) -> dict[str, Any]:
expected = {
"schema": GATE_AMENDMENT_SCHEMA,
"classification": GATE_AMENDMENT_CLASSIFICATION,
"supervisor_task_id": "r42-release-preparation-autonomous-supervisor-20260820",
"supervisor_execution_digest": "491457595667a95809f6f3cd19e9e7c9e74181070fd8038b86f30eb41bdfc231",
"actor_type": "credentialed_host_coordinator",
"operation": "createGovernedTask",
"repository": "aios/aios",
"original_external_request_sha256": "08fc2f3d1b64a2ca11da9f22da48de834cff3442fa7fa2d3bc390f8dbf9b254a",
"external_connector_rule": "ACK_STILL_REQUIRED",
"host_coordinator_rule": "DURABLE_OWNER_GOVERNED_TASK_PROJECT",
"owner_grant_id": policy.grant_id,
"owner_policy_sha256": policy.policy_sha256,
"builder_task_id": "r42-trusted-candidate-builder-runtime-wiring-20260820",
"builder_base_oid": "d957a216a6e9bd4c2292ffc6e44876a1c08fa1e3",
"builder_expected_branch_oid": "d957a216a6e9bd4c2292ffc6e44876a1c08fa1e3",
"builder_target_branch": "agent/r42-trusted-candidate-builder-runtime-wiring-20260820",
"builder_generation": 1,
"builder_idempotency_key": "r42-trusted-candidate-builder-runtime-wiring-binding-v1",
}
value = dict(amendment)
if value != expected:
raise AuthorizationError("GATE_AMENDMENT_BINDING_INVALID", 403)
request_coordinates = {
"task_id": expected["builder_task_id"],
"base_oid": expected["builder_base_oid"],
"expected_branch_oid": expected["builder_expected_branch_oid"],
"target_branch": expected["builder_target_branch"],
"generation": expected["builder_generation"],
"idempotency_key": expected["builder_idempotency_key"],
"repository": expected["repository"],
}
if any(request.get(key) != item for key, item in request_coordinates.items()):
raise AuthorizationError("GATE_AMENDMENT_BUILDER_BINDING_INVALID", 403)
return value
def execute(
self,
capability: str,
request: Mapping[str, Any],
*,
idempotency_key: str,
amendment: Mapping[str, Any] | None = None,
) -> dict[str, Any]:
if not GRANT_RE.fullmatch(idempotency_key):
raise AuthorizationError("IDEMPOTENCY_KEY_INVALID")
policy = self.policy()
decision = policy.authorize(capability, request)
request_sha = decision["request_sha256"]
amendment_value: dict[str, Any] | None = None
if amendment is not None:
if capability != "governed-task:project":
raise AuthorizationError("GATE_AMENDMENT_CAPABILITY_INVALID", 403)
amendment_value = self.validate_gate_amendment(
amendment,
policy=policy,
request=request,
)
prior = self.store.get(idempotency_key)
if prior is not None:
if prior["request_sha256"] != request_sha:
raise AuthorizationError("IDEMPOTENCY_CONFLICT", 409)
response = prior["response"]
if amendment_value is not None:
amendment_result = self.store.put_gate_amendment(
idempotency_key,
amendment_value,
)
response = {
**response,
"gate_amendment": {
"classification": GATE_AMENDMENT_CLASSIFICATION,
"amendment_sha256": amendment_result["amendment_sha256"],
},
}
self.store.update_response(idempotency_key, request_sha, response)
if (
capability == "release:candidate"
and not response.get("projection_id")
@@ -862,6 +1315,15 @@ class Broker:
),
"separate_owner_release_approval_still_required": True,
}
if amendment_value is not None:
amendment_result = self.store.put_gate_amendment(
idempotency_key,
amendment_value,
)
response["gate_amendment"] = {
"classification": GATE_AMENDMENT_CLASSIFICATION,
"amendment_sha256": amendment_result["amendment_sha256"],
}
self.store.put(idempotency_key, capability, request_sha, response)
return response
@@ -881,11 +1343,22 @@ class Handler(BaseHTTPRequestHandler):
def log_message(self, *_args: Any) -> None:
return
def send_json(self, value: Mapping[str, Any], status: int) -> None:
def send_json(
self,
value: Mapping[str, Any],
status: int,
*,
internal_credential: str | None = None,
) -> None:
raw = canonical(value)
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
if internal_credential:
self.send_header(
"X-AIOS-Caller-Projection-Credential",
internal_credential,
)
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
@@ -893,6 +1366,16 @@ class Handler(BaseHTTPRequestHandler):
def caller_account(self) -> str:
return self.headers.get("X-AIOS-Account", "").strip()
def peer_user(self) -> str:
if self.broker is None:
return ""
return peer_user_from_connection(self.connection)
def peer_authorized(self) -> bool:
if self.broker is None:
return False
return authorize_broker_peer(self.connection)
def account_authorized(self) -> bool:
if self.broker is None:
return False
@@ -924,6 +1407,14 @@ class Handler(BaseHTTPRequestHandler):
200,
)
return
try:
peer_ok = self.peer_authorized()
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
return
if not peer_ok:
self.send_json({"error": "SOCKGROUP_NOT_AUTHORIZED"}, 403)
return
if not self.account_authorized():
self.send_json({"error": "ACCOUNT_NOT_AUTHORIZED"}, 403)
return
@@ -938,6 +1429,12 @@ class Handler(BaseHTTPRequestHandler):
"grant_id": policy.grant_id,
"account": policy.account,
"capabilities": sorted(policy.capabilities),
"policy_sha256": policy.policy_sha256,
"constraints": {
"repository": policy.constraints.repository,
"allowed_target_prefix": policy.constraints.allowed_target_prefix,
"max_task_risk": policy.constraints.max_task_risk,
},
"persistent_until_revoked": True,
"separate_owner_release_approval_still_required": True,
}
@@ -957,12 +1454,21 @@ class Handler(BaseHTTPRequestHandler):
self.send_json({"error": exc.code}, exc.status)
def do_POST(self) -> None:
try:
peer_ok = self.peer_authorized()
except AuthorizationError as exc:
self.send_json({"error": exc.code}, exc.status)
return
if not peer_ok:
self.send_json({"error": "SOCKGROUP_NOT_AUTHORIZED"}, 403)
return
if not self.account_authorized():
self.send_json({"error": "ACCOUNT_NOT_AUTHORIZED"}, 403)
return
routes = {
"/v1/project-governed-task": "governed-task:project",
"/v1/authorize-release-candidate": "release:candidate",
"/v1/project-caller": "caller:projection",
}
capability = routes.get(self.path)
if capability is None or self.broker is None:
@@ -970,14 +1476,49 @@ class Handler(BaseHTTPRequestHandler):
return
try:
body = self.body()
if set(body) != {"idempotency_key", "request"}:
if self.path == "/v1/project-caller":
if set(body) != {
"caller",
"scope",
"idempotency_key",
"request",
}:
raise AuthorizationError("CALLER_PROJECTION_FIELDS_INVALID")
if not isinstance(body["request"], Mapping):
raise AuthorizationError("CALLER_PROJECTION_REQUEST_INVALID")
value = self.broker.project_caller(
str(body["caller"]),
str(body["scope"]),
dict(body["request"]),
idempotency_key=str(body["idempotency_key"]),
)
credential = str(value.pop("_credential", ""))
if len(credential) < 32:
raise AuthorizationError("CREDENTIAL_REHYDRATION_MISSING", 503)
self.send_json(
value,
200,
internal_credential=credential,
)
return
if set(body) not in (
{"idempotency_key", "request"},
{"idempotency_key", "request", "amendment"},
):
raise AuthorizationError("BROKER_FIELDS_INVALID")
if not isinstance(body["request"], Mapping):
raise AuthorizationError("BROKER_REQUEST_INVALID")
if "amendment" in body and not isinstance(body["amendment"], Mapping):
raise AuthorizationError("GATE_AMENDMENT_INVALID")
value = self.broker.execute(
capability,
dict(body["request"]),
idempotency_key=str(body["idempotency_key"]),
amendment=(
dict(body["amendment"])
if isinstance(body.get("amendment"), Mapping)
else None
),
)
self.send_json(value, 200)
except AuthorizationError as exc:
@@ -989,6 +1530,41 @@ class UnixHTTPServer(socketserver.ThreadingUnixStreamServer):
def serve() -> None:
credentials = Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
raw_allowed_users = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_USERS",
"",
).strip()
if raw_allowed_users:
allowed_peer_users = tuple(
dict.fromkeys(
item.strip()
for item in raw_allowed_users.split(",")
if item.strip()
)
)
else:
single_allowed_user = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_USER",
"aios-autonomous-agent-worker",
).strip()
allowed_peer_users = (single_allowed_user,) if single_allowed_user else ()
if not allowed_peer_users:
raise RuntimeError("BROKER_ALLOWED_USER_MISSING")
allowed_peer_user = allowed_peer_users[0]
for username in allowed_peer_users:
pwd.getpwnam(username)
allowed_peer_uid = pwd.getpwnam(allowed_peer_user).pw_uid
global CANONICAL_BROKER_GROUP
CANONICAL_BROKER_GROUP = os.environ.get(
"AIOS_OWNER_AUTH_BROKER_ALLOWED_GROUP",
"aios-autonomous-agent-worker",
).strip()
if not CANONICAL_BROKER_GROUP:
raise RuntimeError("BROKER_ALLOWED_GROUP_MISSING")
try:
allowed_peer_gid = grp.getgrnam(CANONICAL_BROKER_GROUP).gr_gid
except KeyError as exc:
raise RuntimeError("BROKER_ALLOWED_GROUP_INVALID") from exc
policy_path = Path(
os.environ.get(
"AIOS_OWNER_DURABLE_AUTHORIZATION_POLICY",
@@ -1029,6 +1605,8 @@ def serve() -> None:
bridge_token=bridge_token,
),
expected_account=account,
expected_peer_user=allowed_peer_user,
allowed_peer_users=frozenset(allowed_peer_users),
)
socket_path = Path(
os.environ.get(
@@ -1044,6 +1622,7 @@ def serve() -> None:
raise RuntimeError("BROKER_SOCKET_CONFLICT")
socket_path.unlink()
server = UnixHTTPServer(str(socket_path), Handler)
os.chown(socket_path, allowed_peer_uid, allowed_peer_gid)
os.chmod(socket_path, 0o660)
try:
server.serve_forever()
@@ -2,13 +2,20 @@
from __future__ import annotations
from dataclasses import dataclass, asdict
from contextlib import contextmanager
import fcntl
import hashlib
import json
from pathlib import Path
import re
from typing import Any, Mapping
from aios_gitops_job_controller import GitOpsJobController, JobStore
from aios_gitops_job_controller import GitOpsJobController, JobStore, sha256_json
from aios_online_agent_execution import (
GovernedExecutionBinding,
project_execution_envelope,
project_online_task,
)
OID_RE = re.compile(r"^[0-9a-f]{40}$")
BRANCH_RE = re.compile(r"^agent/[A-Za-z0-9][A-Za-z0-9._/-]{2,179}$")
@@ -36,35 +43,161 @@ class GovernedTaskBinding:
return {"task_id": self.task_id, "repository": self.repository, "base_oid": self.base_oid, "target_branch": self.target_branch, "expected_branch_oid": self.expected_branch_oid, "generation": self.generation, "branch_mode": self.branch_mode, "create_target_branch": self.branch_mode == "CREATE_NEW", "worktree_mode": "detached_at_base_oid" if self.branch_mode == "CREATE_NEW" else "detached_at_expected_head", "cas_ref_update_required": True, "idempotent": idempotent, "state": "BOUND", "job_id": self.job_id}
def _safe(value: str) -> str:
def _legacy_safe(value: str) -> str:
return re.sub(r"[^A-Za-z0-9_.-]+", "-", value).strip("-")[:120] or "task"
def _task_key(value: str) -> str:
readable = _legacy_safe(value)[:72]
return f"{readable}-{hashlib.sha256(value.encode('utf-8')).hexdigest()[:24]}"
class GovernedTaskBootstrap:
def __init__(self, authority: Any, root: Path, *, remote_name: str, token_path: Path, ssh_key_path: Path, api_base: str = "https://g.cnnetmkt.top/git/api/v1") -> None:
self.authority = authority; self.root = Path(root); self.remote_name = remote_name; self.token_path = Path(token_path); self.ssh_key_path = Path(ssh_key_path); self.api_base = api_base; self.task_meta_root = self.root / "task-bindings"; self.job_root = self.root / "jobs"; self.task_meta_root.mkdir(parents=True, exist_ok=True); self.job_root.mkdir(parents=True, exist_ok=True)
self.authority = authority; self.root = Path(root); self.remote_name = remote_name; self.token_path = Path(token_path); self.ssh_key_path = Path(ssh_key_path); self.api_base = api_base; self.task_meta_root = self.root / "task-bindings"; self.job_root = self.root / "jobs"; self.lock_root = self.root / "task-locks"; self.task_meta_root.mkdir(parents=True, exist_ok=True); self.job_root.mkdir(parents=True, exist_ok=True); self.lock_root.mkdir(parents=True, exist_ok=True)
def _store(self, task_id: str) -> tuple[JobStore, GitOpsJobController]:
root = self.job_root / _safe(task_id); store = JobStore(root, payload_root=root / "payloads"); return store, GitOpsJobController(store)
legacy = self.job_root / _legacy_safe(task_id)
keyed = self.job_root / _task_key(task_id)
root = legacy if legacy.is_dir() and not keyed.exists() else keyed
store = JobStore(root, payload_root=root / "payloads"); return store, GitOpsJobController(store)
def _metadata(self, task_id: str) -> Path:
return self.task_meta_root / (_safe(task_id) + ".json")
return self.task_meta_root / (_task_key(task_id) + ".json")
def _legacy_metadata(self, task_id: str) -> Path:
return self.task_meta_root / (_legacy_safe(task_id) + ".json")
@contextmanager
def _task_lock(self, task_id: str):
path = self.lock_root / (_task_key(task_id) + ".lock")
with path.open("a+b") as handle:
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
@contextmanager
def _branch_lock(self, repository: str, target_branch: str):
key = hashlib.sha256((repository + "\0" + target_branch).encode("utf-8")).hexdigest()
path = self.lock_root / ("branch-" + key + ".lock")
with path.open("a+b") as handle:
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
def _read_existing(self, task_id: str) -> GovernedTaskBinding | None:
path = self._metadata(task_id)
if not path.exists():
path = self._legacy_metadata(task_id)
if not path.is_file() or path.is_symlink(): return None
return GovernedTaskBinding(**json.loads(path.read_text(encoding="utf-8")))
binding = GovernedTaskBinding(**json.loads(path.read_text(encoding="utf-8")))
if binding.task_id != task_id:
raise BootstrapError("TASK_BINDING_KEY_COLLISION")
return binding
def get_binding(self, task_id: str) -> GovernedTaskBinding:
"""Return the exact durable binding; never synthesize missing lineage."""
existing = self._read_existing(str(task_id))
if existing is None or not existing.job_id:
raise BootstrapError("TASK_EXECUTION_PROJECTION_MISSING")
return existing
def execution_binding(self, binding: GovernedTaskBinding) -> GovernedExecutionBinding:
expected = binding.expected_branch_oid
mode = binding.branch_mode
if mode == "CREATE_NEW":
current = self.authority.read_ref("refs/heads/" + binding.target_branch)
if current is not None:
store, _ = self._store(binding.task_id)
with store._connect() as connection:
rows = connection.execute(
"SELECT current_head_oid,generation,state,lineage_anchor_oid "
"FROM branch_leases WHERE job_id=? AND branch=? AND state='ACTIVE' "
"ORDER BY revision DESC",
(binding.job_id, binding.target_branch),
).fetchall()
if len(rows) != 1:
raise BootstrapError("SAME_JOB_CONTINUATION_LEASE_INVALID")
row = rows[0]
if (
str(row["current_head_oid"] or "") != current
or int(row["generation"] or 0) != binding.generation
or str(row["lineage_anchor_oid"] or "") != binding.base_oid
):
raise BootstrapError("SAME_JOB_CONTINUATION_BINDING_INVALID")
expected = current
mode = "CONTINUE_EXISTING"
return GovernedExecutionBinding.from_mapping(
{
"task_id": binding.task_id,
"job_id": binding.job_id,
"repository": binding.repository,
"base_oid": binding.base_oid,
"expected_branch_oid": expected,
"target_branch": binding.target_branch,
"branch_mode": mode,
"generation": binding.generation,
"idempotency_key": binding.idempotency_key,
}
)
def execution_envelope(
self,
task_id: str,
execution_request: Mapping[str, Any] | None = None,
*,
required_capabilities: tuple[str, ...] = (),
expected_job_id: str | None = None,
) -> dict[str, Any]:
binding = self.get_binding(task_id)
if expected_job_id and binding.job_id != expected_job_id:
raise BootstrapError("JOB_BINDING_CONFLICT")
return project_execution_envelope(
self.execution_binding(binding),
execution_request,
required_capabilities=required_capabilities,
)
def online_task_projection(
self,
task_id: str,
task_request: Mapping[str, Any],
execution_request: Mapping[str, Any] | None = None,
*,
expected_job_id: str | None = None,
) -> dict[str, Any]:
binding = self.get_binding(task_id)
if expected_job_id and binding.job_id != expected_job_id:
raise BootstrapError("JOB_BINDING_CONFLICT")
return project_online_task(self.execution_binding(binding), task_request, execution_request)
def _write(self, binding: GovernedTaskBinding) -> None:
path = self._metadata(binding.task_id); temporary = path.with_suffix(".tmp"); temporary.write_text(json.dumps(asdict(binding), sort_keys=True), encoding="utf-8"); temporary.replace(path)
def create(self, request: Mapping[str, Any]) -> dict[str, Any]:
task_id = str(request.get("task_id") or "")
if not task_id:
raise BootstrapError("TASK_BINDING_INVALID")
repository = str(request.get("repository") or "")
target_branch = str(request.get("target_branch") or "")
with self._task_lock(task_id):
with self._branch_lock(repository, target_branch):
return self._create_locked(request)
def _create_locked(self, request: Mapping[str, Any]) -> dict[str, Any]:
values = {key: str(request[key]) for key in ("task_id", "repository", "base_oid", "target_branch", "risk", "description", "idempotency_key")}
if values["repository"] != "aios/aios" or not OID_RE.fullmatch(values["base_oid"]): raise BootstrapError("TASK_BINDING_INVALID")
if not BRANCH_RE.fullmatch(values["target_branch"]): raise BootstrapError("TARGET_BRANCH_INVALID")
existing = self._read_existing(values["task_id"])
if existing is not None:
if any(getattr(existing, key) != values[key] for key in ("repository", "base_oid", "target_branch", "risk", "description")): raise BootstrapError("TASK_BINDING_CONFLICT")
if any(getattr(existing, key) != values[key] for key in ("repository", "base_oid", "target_branch", "risk", "description", "idempotency_key")): raise BootstrapError("TASK_BINDING_CONFLICT")
optional = {"expected_branch_oid": existing.expected_branch_oid, "branch_mode": existing.branch_mode, "generation": existing.generation}
for key, durable in optional.items():
if key in request and str(request[key]) != str(durable): raise BootstrapError("TASK_BINDING_CONFLICT")
return existing.response(idempotent=True)
if not self.authority.commit_exists(values["base_oid"]): raise BootstrapError("BASE_COMMIT_NOT_FOUND")
current = self.authority.read_ref("refs/heads/" + values["target_branch"])
@@ -74,8 +207,61 @@ class GovernedTaskBootstrap:
if expected != values["base_oid"]: raise BootstrapError("EXPECTED_BASE_CONFLICT")
else:
if expected != current: raise BootstrapError("EXISTING_BRANCH_OID_CONFLICT")
mode, generation = "CONTINUE_EXISTING", int(request.get("generation", 2))
if generation < 1: raise BootstrapError("GENERATION_INVALID")
parent_task_id = str(request.get("parent_task_id") or "")
parent_job_id = str(request.get("parent_job_id") or "")
if not parent_task_id or not parent_job_id:
raise BootstrapError("EXISTING_BRANCH_OWNERSHIP_REQUIRED")
parent = self._read_existing(parent_task_id)
if parent is None or parent.job_id != parent_job_id or parent.repository != values["repository"] or parent.target_branch != values["target_branch"]:
raise BootstrapError("EXISTING_BRANCH_OWNERSHIP_CONFLICT")
store, _ = self._store(parent_task_id)
with store._connect() as connection:
rows = connection.execute(
"SELECT current_head_oid,generation,state FROM branch_leases WHERE job_id=? AND branch=? AND state='ACTIVE' ORDER BY revision DESC",
(parent_job_id, values["target_branch"]),
).fetchall()
if len(rows) != 1 or str(rows[0]["current_head_oid"] or "") != current:
raise BootstrapError("EXISTING_BRANCH_OWNERSHIP_CONFLICT")
mode, generation = "CONTINUE_EXISTING", int(rows[0]["generation"]) + 1
if "generation" in request and int(request["generation"]) != generation:
raise BootstrapError("GENERATION_BINDING_CONFLICT")
online_request = request.get("online_task")
execution_request = request.get("execution")
projection_required = (
online_request is not None
or execution_request is not None
or request.get("require_online_task_projection") is True
)
if projection_required:
if not isinstance(online_request, Mapping):
raise BootstrapError("ONLINE_TASK_REQUEST_INVALID")
if execution_request is not None and not isinstance(execution_request, Mapping):
raise BootstrapError("EXECUTION_REQUEST_INVALID")
provisional_job_id = "job-" + sha256_json(
{"client_idempotency_key": values["idempotency_key"]}
)[:32]
try:
provisional_binding = GovernedExecutionBinding.from_mapping(
{
"task_id": values["task_id"],
"job_id": provisional_job_id,
"repository": values["repository"],
"base_oid": values["base_oid"],
"expected_branch_oid": expected,
"target_branch": values["target_branch"],
"branch_mode": mode,
"generation": generation,
"idempotency_key": values["idempotency_key"],
}
)
project_online_task(
provisional_binding,
dict(online_request),
dict(execution_request or {}),
)
except Exception as exc:
raise BootstrapError("EXECUTION_BINDING_INVALID") from exc
payload = json.dumps(values, sort_keys=True, separators=(",", ":")).encode()
_, controller = self._store(values["task_id"])
receipt = controller.submit({"idempotency_key": values["idempotency_key"], "campaign_id": values["task_id"], "task_id": values["task_id"], "repository": values["repository"], "operation": "GOVERNED_TASK_CREATE", "branch": values["target_branch"], "expected_branch_oid": expected, "payload_digest": hashlib.sha256(payload).hexdigest(), "lineage_anchor_oid": values["base_oid"]}, payload=payload)
+375 -22
View File
@@ -3,6 +3,15 @@ from __future__ import annotations
import hashlib, hmac, json, os, re, subprocess, sys, time, urllib.parse, urllib.request, uuid
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from aios_caller_admission_projection import (
CALLER,
SCOPE,
GOVERNED_TASK_SCOPE,
PROJECTED_SCOPES,
CallerProjectionClient,
CallerProjectionError,
)
from baseline_authority import BaselineAuthority, BaselineAuthorityError
from typing import Any
@@ -89,6 +98,11 @@ SILENT_RELEASE_PUBLIC_KEYS = Path(os.getenv(
'AIOS_SILENT_RELEASE_PUBLIC_KEYS',
'/etc/aios-gitops/silent-release-owner-public-keys.json',
))
CALLER_PROJECTION_SOCKET = Path(os.getenv(
'AIOS_OWNER_AUTH_BROKER_SOCKET',
'/run/aios-owner-authorization-broker/broker.sock',
))
CALLER_PROJECTION_CLIENT = CallerProjectionClient(CALLER_PROJECTION_SOCKET)
monitor = ActionMonitor()
BASE_MODELS = [
@@ -112,6 +126,9 @@ READ_ONLY_GET_PATHS = frozenset({
'/admin/ai-evolution/api/data',
'/api/release-consistency', '/api/tag-publication-status',
'/api/release-acceptance',
'/api/governed-write-capabilities', '/api/control-plane-snapshot',
'/api/caller-capabilities',
'/api/online-agent-task', '/api/online-agent-task-events',
'/getReleaseCandidate', '/getReleasePlan', '/getReleaseAcceptance',
'/getApprovalEnvelope', '/getReleaseSaga', '/getCapabilityStatus',
'/getRecoveryStatus',
@@ -124,6 +141,25 @@ SILENT_RELEASE_POST_PATHS = frozenset({
'/startDeployment',
})
GOVERNED_EXECUTION_CAPABILITIES = frozenset({
"governed.write.branch",
"governed.write.file",
})
def requires_governed_execution(value: Mapping[str, Any]) -> bool:
capabilities = value.get("required_capabilities")
if capabilities is None:
return False
if not isinstance(capabilities, list):
return True
return bool(
GOVERNED_EXECUTION_CAPABILITIES.intersection(
{str(item) for item in capabilities}
)
)
def reject_non_read_http(handler, method: str, path: str) -> bool:
if method == 'GET':
return False
@@ -407,14 +443,113 @@ class H(BaseHTTPRequestHandler):
except Exception: return {'_raw':raw}
def actor(self):
auth=self.headers.get('Authorization',''); tok=auth[7:].strip() if auth.startswith('Bearer ') else self.headers.get('X-AIOS-Token','')
try:
rec=authenticate(tok)
if rec: return {'ok':True,'account':safe(rec.get('account') or rec.get('name') or 'token-user'),'role':rec.get('role','user'),'scopes':set(rec.get('scopes') or [])}
return {'ok':False,'account':'anonymous','role':'anonymous','scopes':set()}
def need(self, scope):
except Exception:
rec=None
if not isinstance(rec, dict) or not rec:
return {
'ok': False,
'account': 'anonymous',
'role': 'anonymous',
'scopes': set(),
'static_scopes': set(),
'projection': None,
'credential_loaded': False,
}
scopes = set(rec.get('scopes') or [])
return {
'ok': True,
'account': safe(rec.get('account') or rec.get('name') or 'token-user'),
'role': rec.get('role', 'user'),
'scopes': scopes,
'static_scopes': set(scopes),
'projection': None,
'credential_loaded': False,
}
def need(self, scope, *, projection_request=None):
a = self.actor()
if not a['ok']: self.sendj({'error':'unauthorized','hint':'Bearer token required'},401); return None
if '*' not in a['scopes'] and scope not in a['scopes']: self.sendj({'error':'forbidden','required_scope':scope,'account':a['account']},403); return None
if not a['ok']:
self.sendj({'error': 'unauthorized', 'hint': 'Bearer token required'}, 401)
return None
if a['account'] == CALLER and scope in PROJECTED_SCOPES:
try:
result = CALLER_PROJECTION_CLIENT.rehydrate(
a['account'],
scope,
projection_request,
)
except CallerProjectionError as exc:
self.sendj(
{
'error': 'caller_projection_denied',
'projection_state': 'DENIED',
'detail': exc.code,
'account': a['account'],
},
exc.status,
)
return None
projection = result.public
a['scopes'].update(projection.get('effective_scopes') or [])
a['projection'] = projection
a['credential_loaded'] = result.credential_loaded
if '*' not in a['scopes'] and scope not in a['scopes']:
self.sendj(
{
'error': 'forbidden',
'required_scope': scope,
'account': a['account'],
},
403,
)
return None
return a
def caller_capabilities(self):
actor = self.actor()
if not actor['ok']:
return self.sendj(
{'error': 'unauthorized', 'hint': 'Bearer token required'},
401,
)
effective = set(actor['static_scopes'])
projection_state = 'NOT_REQUIRED'
credential_state = 'NOT_REQUIRED'
rehydration_state = 'NOT_APPLICABLE'
admitted = True
projection_error = None
if actor['account'] == CALLER:
try:
result = CALLER_PROJECTION_CLIENT.rehydrate(
actor['account'],
GOVERNED_TASK_SCOPE,
{},
)
except CallerProjectionError as exc:
admitted = False
projection_state = 'DENIED'
credential_state = 'NOT_REHYDRATED'
rehydration_state = 'FAILED'
projection_error = exc.code
else:
effective.update(result.public.get('effective_scopes') or [])
projection_state = result.public['projection_state']
credential_state = result.public['credential_state']
rehydration_state = result.public['rehydration_state']
return self.sendj(
{
'schema': 'aios.caller-capability-introspection.v1',
'caller_identity': actor['account'],
'admitted': admitted,
'effective_scopes': sorted(effective),
'projection_state': projection_state,
'credential_state': credential_state,
'rehydration_state': rehydration_state,
'projection_error': projection_error,
'secret_exposed': False,
}
)
def bridge_release(self, path, data):
route = {
'/createTagApprovalRequest': '/v2/tag/approval-request',
@@ -446,24 +581,183 @@ class H(BaseHTTPRequestHandler):
return self.sendj(body, exc.code)
except (urllib.error.URLError, TimeoutError) as exc:
return self.sendj({'error': 'GOVERNED_BRIDGE_UNAVAILABLE', 'detail': type(exc).__name__}, 503)
def do_POST(self):
path = urllib.parse.urlparse(self.path).path
if path in {
'/createTagApprovalRequest', '/requestOrCreateTag', '/getTagPublicationStatus',
'/buildProductionArtifact', '/executeApprovedProductionRelease',
'/getReleaseConsistency', '/runReleaseAcceptance',
}:
actor = self.need('tag:create')
if not actor:
return
return self.bridge_release(path, self.body())
if reject_non_read_http(self, 'POST', path):
return
return self.sendj({'error': 'not_found', 'path': path}, 404)
def bridge_v2_json(self, route, data=None, method='POST'):
base = os.getenv('AIOS_BRIDGE_V2_URL', 'http://127.0.0.1:18892').rstrip('/')
payload = None if data is None else json.dumps(
data, sort_keys=True, separators=(',', ':')
).encode('utf-8')
request = urllib.request.Request(
base + route,
data=payload,
headers={'Content-Type': 'application/json'} if payload is not None else {},
method=method,
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
result = json.loads(response.read().decode('utf-8'))
return self.sendj(result, response.status)
except urllib.error.HTTPError as exc:
try:
body = json.loads(exc.read().decode('utf-8'))
except Exception:
body = {'error': 'bridge_http_error'}
return self.sendj(body, exc.code)
except (urllib.error.URLError, TimeoutError) as exc:
return self.sendj(
{'error': 'GOVERNED_BRIDGE_UNAVAILABLE', 'detail': type(exc).__name__},
503,
)
def control_plane_snapshot(self):
"""Build a non-sensitive snapshot locally when Bridge v2 has no snapshot HTTP route."""
base = os.getenv('AIOS_BRIDGE_V2_URL', 'http://127.0.0.1:18892').rstrip('/')
def bridge_get(route):
request = urllib.request.Request(
base + route,
headers={'Accept': 'application/json'},
method='GET',
)
with urllib.request.urlopen(request, timeout=15) as response:
if response.status != 200:
raise RuntimeError('bridge_read_http_' + str(response.status))
value = json.loads(response.read().decode('utf-8'))
if not isinstance(value, dict):
raise RuntimeError('bridge_read_not_object')
return value
try:
runtime = bridge_get('/v2/runtime')
capabilities = bridge_get('/v2/capabilities')
repository = repository_snapshot(
git_dir=AUTHORITATIVE_GIT_DIR,
repository_identity=REPOSITORY_IDENTITY,
)
if not repository.get('repository_available'):
return self.sendj({
'error': 'AUTHORITATIVE_REPOSITORY_UNAVAILABLE',
'query_error': repository.get('query_error'),
}, 503)
baseline = dict(BASELINE_AUTHORITY.read())
except BaselineAuthorityError as exc:
return self.sendj({
'error': 'RECONCILIATION_REQUIRED',
'detail': str(exc),
}, 503)
except urllib.error.HTTPError as exc:
return self.sendj({
'error': 'GOVERNED_BRIDGE_UNAVAILABLE',
'detail': 'HTTP_' + str(exc.code),
}, 503)
except (urllib.error.URLError, TimeoutError, OSError, ValueError, RuntimeError, json.JSONDecodeError) as exc:
return self.sendj({
'error': 'GOVERNED_BRIDGE_UNAVAILABLE',
'detail': type(exc).__name__,
}, 503)
legacy = capabilities.get('legacy_writes_enabled')
if legacy is None:
legacy = capabilities.get('legacy_global_writes_enabled')
payload = {
'schema': 'aios.control-plane-snapshot.aggregate.v1',
'snapshot_source': 'model-api-local-aggregate',
'version': {
'target_release': runtime.get('control_plane_release_version'),
},
'runtime': {
'bridge_schema_version': runtime.get('bridge_schema_version'),
'control_plane_release_version': runtime.get('control_plane_release_version'),
'runtime_release_commit': runtime.get('runtime_release_commit'),
'runtime_path': runtime.get('runtime_path'),
'artifact_sha256': runtime.get('artifact_sha256'),
'drift_status': runtime.get('drift_status'),
'deployment_state': runtime.get('deployment_state'),
},
'repository': {
'identity': runtime.get('repository_identity') or runtime.get('repository') or REPOSITORY_IDENTITY,
'authority_git_dir': runtime.get('authoritative_git_dir') or repository.get('authoritative_git_dir'),
'authority_main_oid': repository.get('authoritative_head_oid'),
},
'production': {
'baseline_version': baseline.get('current_baseline'),
'target_oid': baseline.get('target_oid'),
'deployment_state': baseline.get('status'),
'baseline_generation': baseline.get('generation'),
'baseline_sha256': baseline.get('authority_sha256'),
},
'task': {},
'capabilities': {
'legacy_writes_enabled': legacy,
'governed_writes_enabled': capabilities.get('governed_writes_enabled'),
'force_push': capabilities.get('force_push'),
'main_write': capabilities.get('main_write'),
'tag_write': capabilities.get('tag_write'),
'baseline_write': capabilities.get('baseline_write'),
'production_write': capabilities.get('production_write'),
},
'credential_broker': {
'status': 'SERVER_SIDE_ONLY',
'provider': 'BridgeV2RuntimeCredentialProjection',
},
'secret_material_exposed': False,
}
return self.sendj(payload)
def agent_task_control(self, method, route, data=None):
token_path = Path(os.getenv(
'AIOS_AGENT_TASK_CONTROL_TOKEN_FILE',
'/run/credentials/aios-gitops-model-api-v2r2c1-staging.service/agent_task_control_token',
))
if token_path.is_symlink() or not token_path.is_file():
return self.sendj({'error': 'AGENT_TASK_CONTROL_CREDENTIAL_UNAVAILABLE'}, 503)
token = token_path.read_text(encoding='utf-8').strip()
if len(token) < 32:
return self.sendj({'error': 'AGENT_TASK_CONTROL_CREDENTIAL_INVALID'}, 503)
base = os.getenv('AIOS_AGENT_TASK_API_URL', 'http://127.0.0.1:18940').rstrip('/')
payload = None if data is None else json.dumps(
data, sort_keys=True, separators=(',', ':')
).encode('utf-8')
headers = {'Authorization': 'Bearer ' + token, 'Accept': 'application/json'}
if payload is not None:
headers['Content-Type'] = 'application/json'
request = urllib.request.Request(
base + route, data=payload, headers=headers, method=method
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
result = json.loads(response.read().decode('utf-8'))
return self.sendj(result, response.status)
except urllib.error.HTTPError as exc:
try:
body = json.loads(exc.read().decode('utf-8'))
except Exception:
body = {'error': 'agent_task_http_error'}
return self.sendj(body, exc.code)
except (urllib.error.URLError, TimeoutError) as exc:
return self.sendj(
{'error': 'AGENT_TASK_API_UNAVAILABLE', 'detail': type(exc).__name__},
503,
)
def do_GET(self):
u=urllib.parse.urlparse(self.path); path=u.path.rstrip('/') if u.path != '/' else u.path
if path not in READ_ONLY_GET_PATHS: return self.sendj({'error':'not_found','path':path},404)
q=urllib.parse.parse_qs(u.query)
if path == '/api/governed-write-capabilities':
return self.bridge_v2_json('/v2/capabilities', method='GET')
if path == '/api/control-plane-snapshot':
return self.control_plane_snapshot()
if path == '/api/caller-capabilities':
return self.caller_capabilities()
if path in ('/api/online-agent-task', '/api/online-agent-task-events'):
actor = self.need('agent-task:control')
if not actor:
return
task_id = (q.get('task_id') or [''])[0]
if not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._:-]{2,179}', task_id):
return self.sendj({'error': 'task_id_invalid'}, 400)
suffix = '/events' if path.endswith('-events') else ''
return self.agent_task_control('GET', '/v1/tasks/' + task_id + suffix)
try:
if path in ('/health','/api/health'):
return self.sendj({
@@ -700,12 +994,68 @@ class H(BaseHTTPRequestHandler):
return self.sendj({'error':'not_found','path':path},404)
def do_POST(self):
path=urllib.parse.urlparse(self.path).path.rstrip('/')
governed_routes = {
'/createGovernedTask': ('/v2/governed-task', 'branch:create'),
'/acquireWriterLease': ('/v2/writer-lease', 'file:write'),
'/commitFileGoverned': ('/v2/commit-file', 'file:write'),
'/commitFilesGoverned': ('/v2/commit-files', 'file:write'),
'/getGitOpsJob': ('/v2/job', 'file:write'),
'/resumeGitOpsJob': ('/v2/job/resume', 'file:write'),
'/reconcileGitOpsJob': ('/v2/job/reconcile', 'file:write'),
'/createTagApprovalRequest': ('/v2/tag/approval-request', 'tag:create'),
'/requestOrCreateTag': ('/v2/tag/publish', 'tag:create'),
'/getTagPublicationStatus': ('/v2/tag/status', 'tag:create'),
'/buildProductionArtifact': ('/v2/release/artifact', 'tag:create'),
'/executeApprovedProductionRelease': ('/v2/release/execute', 'tag:create'),
'/getReleaseConsistency': ('/v2/release/consistency', 'tag:create'),
'/runReleaseAcceptance': ('/v2/release/acceptance', 'tag:create'),
}
if path in governed_routes:
route, scope = governed_routes[path]
if path == '/createGovernedTask' and self.actor()['account'] == CALLER:
scope = GOVERNED_TASK_SCOPE
actor = self.need(scope)
if not actor:
return
return self.bridge_v2_json(route, self.body(), method='POST')
if path in {
'/createOnlineAgentTask', '/registerOnlineAgent',
'/verifyOnlineAgentTask', '/cancelOnlineAgentTask',
}:
actor = self.need('agent-task:control')
if not actor:
return
data = self.body()
if path == '/createOnlineAgentTask':
if requires_governed_execution(data):
return self.sendj(
{
'error': 'governed_execution_binding_required',
'hint': 'use createGovernedTask',
},
409,
)
return self.agent_task_control('POST', '/v1/tasks', data)
if path == '/registerOnlineAgent':
return self.agent_task_control('POST', '/v1/agents/register', data)
task_id = str(data.pop('task_id', '') or '')
if not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._:-]{2,179}', task_id):
return self.sendj({'error': 'task_id_invalid'}, 400)
suffix = '/verify' if path == '/verifyOnlineAgentTask' else '/cancel'
return self.agent_task_control('POST', '/v1/tasks/' + task_id + suffix, data)
if reject_non_read_http(self, 'POST', path): return
data=self.body()
try:
if path == '/createReleaseCandidate':
a=self.need('release:candidate')
if not a: return
a = self.need(
'release:candidate',
projection_request={
'source_commit': str(data.get('source_commit') or ''),
'version': str(data.get('version') or ''),
},
)
if not a:
return
return self.sendj(
silent_release_service().create_release_candidate(data)
)
@@ -996,7 +1346,7 @@ def api_spec():
return {
'version': 'V2R2C1-IMMUTABLE-STAGING',
'openapi': '/api/openapi.yaml',
'read': ['GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run','GET /getReleaseCandidate','GET /getApprovalEnvelope','GET /getReleaseSaga','GET /admin/ai-evolution'],
'read': ['GET /api/caller-capabilities','GET /api/base','GET /api/ver','GET /api/diff','GET /api/patch','GET /api/model','GET /api/run','GET /getReleaseCandidate','GET /getApprovalEnvelope','GET /getReleaseSaga','GET /admin/ai-evolution'],
'write': ['POST /api/branch','POST /api/file','POST /api/commit','POST /api/patch','POST /api/tag','POST /api/rollback','POST /api/model','PUT /api/model-priority','POST /api/nl','POST /createReleaseCandidate','POST /submitOwnerApproval','POST /startDeployment'],
'policy': {
'bearer_required_for_write': True,
@@ -1011,6 +1361,9 @@ def api_spec():
'silent_release_shadow_default': True,
'runtime_mode': 'IMMUTABLE_STAGING',
'governed_release_routing': 'bridge-v2',
'caller_admission_projection': 'durable-owner-policy-server-side-ack',
'caller_projection_scope': SCOPE,
'caller_projection_caller': CALLER,
},
}
@@ -179,10 +179,7 @@ def _agent_runtime_input(value: object) -> dict[str, Any]:
if not isinstance(value, Mapping):
_error("AGENT_RUNTIME_INPUT_REQUIRED")
raw = dict(value)
allowed = {
"goal", "instructions", "success_criteria", "constraints",
"scope", "trace_id",
}
allowed = {"goal", "instructions", "success_criteria", "constraints"}
if set(raw) - allowed:
_error("AGENT_RUNTIME_INPUT_FIELDS_INVALID")
goal = _string(raw.get("goal"), "AGENT_RUNTIME_GOAL_REQUIRED")
@@ -211,34 +208,11 @@ def _agent_runtime_input(value: object) -> dict[str, Any]:
lowered = str(key).lower()
if any(marker in lowered for marker in sensitive_markers):
_error("AGENT_RUNTIME_SECRET_FIELD_DENY")
scope = raw.get("scope", {})
if not isinstance(scope, Mapping):
_error("AGENT_RUNTIME_SCOPE_INVALID")
scope = {
"scope_type": str(scope.get("scope_type") or "legacy"),
"tenant_id": scope.get("tenant_id"),
"project_id": scope.get("project_id"),
}
if scope["scope_type"] not in {"legacy", "system", "tenant"}:
_error("AGENT_RUNTIME_SCOPE_INVALID")
if scope["scope_type"] == "tenant":
if not isinstance(scope["tenant_id"], str) or not scope["tenant_id"].strip():
_error("AGENT_RUNTIME_SCOPE_INVALID")
elif scope["tenant_id"] not in (None, "") or scope["project_id"] not in (None, ""):
_error("AGENT_RUNTIME_SCOPE_INVALID")
trace_id = str(raw.get("trace_id") or "").strip()
if not trace_id or len(trace_id) > 255:
_error("AGENT_RUNTIME_TRACE_ID_INVALID")
normalized = {
"goal": goal[:12000],
"instructions": instructions[:32000],
"success_criteria": normalized_criteria,
"constraints": dict(constraints),
"scope": scope,
"trace_id": trace_id,
}
if len(_canonical(normalized).encode("utf-8")) > MAX_AGENT_RUNTIME_INPUT_BYTES:
_error("AGENT_RUNTIME_INPUT_TOO_LARGE")
@@ -451,12 +425,6 @@ def project_online_task(
if adapter not in AGENT_RUNTIME_ADAPTERS:
_error("AGENT_RUNTIME_ADAPTER_INVALID")
runtime_input = execution_value.get("runtime_input")
tenant_id = request.get("tenant_id")
project_id = request.get("project_id")
declared_scope = str(
request.get("scope_type")
or ("tenant" if isinstance(tenant_id, str) and tenant_id.strip() else "legacy")
)
if runtime_input is None:
runtime_input = {
"goal": request.get("goal"),
@@ -464,20 +432,6 @@ def project_online_task(
"success_criteria": request.get("success_criteria") or [],
"constraints": request.get("constraints") or {},
}
elif not isinstance(runtime_input, Mapping):
_error("AGENT_RUNTIME_INPUT_REQUIRED")
runtime_input = dict(runtime_input)
# Scope/trace are authoritative Task identity, never caller-selected runtime hints.
runtime_input["scope"] = {
"scope_type": declared_scope,
"tenant_id": tenant_id,
"project_id": project_id,
}
runtime_input["trace_id"] = str(
request.get("trace_id")
or request.get("run_id")
or binding.task_id
)
execution_value["runtime_input"] = runtime_input
execution = project_execution_envelope(
binding,
+28 -38
View File
@@ -1910,6 +1910,8 @@ class WorkerSupervisor:
# engineering placeholder: never pass that stale payload back to the
# resolver, and never claim it until a deterministic plan exists.
pending_plan = source_capture_requires_deterministic_plan(persisted)
if not pending_plan:
return persisted, "persisted_verified"
projection_input = (
execution
if persisted.action in {"source_capture", "agent_runtime"} and not pending_plan
@@ -1958,25 +1960,7 @@ class WorkerSupervisor:
phase="projection",
)
return persisted, "persisted_verified"
status, projected = self.bridge.project_execution(task)
if status == 404 or projected.get("error") == "TASK_EXECUTION_PROJECTION_MISSING":
raise WorkerError("TASK_SKIPPED_PROJECTION_MISSING", phase="projection")
if not 200 <= status < 300:
raise WorkerError(
"TASK_SKIPPED_POLICY_DENY",
phase="projection",
details={"api_status": status, "error": projected.get("error")},
)
envelope = ExecutionEnvelope.from_task(task, projected, WORKER_CAPABILITIES)
audit(
"TASK_EXECUTION_ENVELOPE_PROJECTED",
task_id=envelope.task_id,
job_id=envelope.job_id,
generation=envelope.generation,
action=envelope.action,
execution_digest=envelope.digest,
)
return envelope, "recovered_projection"
def process_claim(
self,
@@ -2448,23 +2432,21 @@ class WorkerSupervisor:
write_worker_liveness("POLL_OK", visible_count=len(tasks))
audit("TASK_POLL", visible_count=len(tasks))
except WorkerError as exc:
write_worker_liveness(
"POLL_FAILED",
error=exc.code,
)
write_worker_liveness("POLL_FAILED", error=exc.code)
audit("TASK_POLL_FAILED", error=exc.code, phase=exc.phase, details=exc.details)
return 10
dynamic_tasks: list[tuple[dict[str, Any], ExecutionEnvelope]] = []
dynamic_tasks: list[tuple[dict[str, Any], ExecutionEnvelope, str]] = []
for task in tasks:
try:
envelope, source = self.resolve_envelope(task)
metadata = task.get("metadata")
execution = metadata.get("execution") if isinstance(metadata, Mapping) else None
if not isinstance(execution, Mapping):
raise WorkerError("TASK_SKIPPED_PROJECTION_MISSING", phase="projection")
envelope = ExecutionEnvelope.from_task(task, worker_capabilities=WORKER_CAPABILITIES)
source = "pending_plan" if source_capture_requires_deterministic_plan(envelope) else "persisted_verified"
except WorkerError as exc:
event = "TASK_SKIPPED_ENVELOPE_INVALID"
if exc.code in {
"TASK_SKIPPED_PROJECTION_MISSING",
"TASK_SKIPPED_POLICY_DENY",
"TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN",
}:
if exc.code in {"TASK_SKIPPED_PROJECTION_MISSING", "TASK_SKIPPED_POLICY_DENY", "TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN"}:
event = exc.code
elif exc.code == "CAPABILITY_MISMATCH_DENY":
event = "TASK_SKIPPED_CAPABILITY_MISMATCH"
@@ -2472,16 +2454,11 @@ class WorkerSupervisor:
continue
audit("TASK_VISIBLE_TO_WORKER_CLAIMABLE", task_id=task.get("task_id"), envelope_source=source, action=envelope.action)
if envelope.adapter not in WORKER_ALLOWED_ADAPTERS:
audit(
"TASK_SKIPPED_ADAPTER_MISMATCH",
task_id=task.get("task_id"),
adapter=envelope.adapter,
allowed_adapters=sorted(WORKER_ALLOWED_ADAPTERS),
)
audit("TASK_SKIPPED_ADAPTER_MISMATCH", task_id=task.get("task_id"), adapter=envelope.adapter, allowed_adapters=sorted(WORKER_ALLOWED_ADAPTERS))
continue
dynamic_tasks.append((task, envelope))
dynamic_tasks.sort(key=_claim_order)
for task, envelope in dynamic_tasks:
dynamic_tasks.append((task, envelope, source))
dynamic_tasks.sort(key=lambda item: _claim_order(item[0]))
for task, envelope, source in dynamic_tasks:
task_id = str(task.get("task_id") or "")
status, claim = self.agent.claim(task_id)
if status == 409:
@@ -2490,6 +2467,19 @@ class WorkerSupervisor:
if status != 200:
audit("TASK_CLAIM_FAILED", task_id=task_id, api_status=status, error=claim.get("error"))
continue
if source == "pending_plan":
current_status, current_task = self.agent.get(task_id)
if current_status == 200:
try:
envelope, _ = self.resolve_envelope(current_task)
except WorkerError as exc:
audit("TASK_PLAN_RESOLUTION_FAILED", task_id=task_id, reason=exc.code, phase=exc.phase, details=exc.details)
self.process_claim(task, claim, envelope)
return 0
else:
audit("TASK_READ_AFTER_CLAIM_FAILED", task_id=task_id, api_status=current_status)
self.process_claim(task, claim, envelope)
return 0
self.process_claim(task, claim, envelope)
return 0
return 0
@@ -0,0 +1,287 @@
"""Credential-isolated durable coordinator for the release-preparation supervisor.
The coordinator owns only its Online Task agent credential. It never forwards
that credential to Hermes/OpenClaw and never submits a Task result while the
supervision goal is incomplete. Its job is deliberately small: claim the one
fixed supervisor Task after an exact coordinator projection and keep a durable
heartbeat lease while later phases observe and repair through governed APIs.
"""
from __future__ import annotations
from datetime import datetime, timezone
import json
import os
from pathlib import Path
import tempfile
import time
from typing import Any, Mapping
import urllib.error
import urllib.request
API_BASE = os.environ.get(
"AIOS_AGENT_TASK_API_URL", "http://127.0.0.1:18940"
).rstrip("/")
AGENT_ID = os.environ.get(
"AIOS_AGENT_ID",
"online-engineering-worker-cn-network-mkt-autonomous-20260815",
)
TASK_ID = os.environ.get(
"AIOS_RELEASE_PREPARATION_SUPERVISOR_TASK_ID",
"r42-release-preparation-autonomous-supervisor-20260820",
)
EXPECTED_EXECUTION_DIGEST = os.environ.get(
"AIOS_RELEASE_PREPARATION_SUPERVISOR_EXECUTION_DIGEST", ""
)
RUNTIME_CAPABILITY = "agent.runtime.openclaw"
COORDINATOR_CAPABILITY = "engineering.autonomous"
REQUIRED_CAPABILITIES = [RUNTIME_CAPABILITY, COORDINATOR_CAPABILITY]
POLL_SECONDS = int(os.environ.get("AIOS_SUPERVISOR_COORDINATOR_POLL_SECONDS", "5"))
HEARTBEAT_SECONDS = int(
os.environ.get("AIOS_SUPERVISOR_COORDINATOR_HEARTBEAT_SECONDS", "10")
)
LEASE_SECONDS = int(os.environ.get("AIOS_SUPERVISOR_COORDINATOR_LEASE_SECONDS", "300"))
STATE_ROOT = Path(
os.environ.get(
"AIOS_SUPERVISOR_COORDINATOR_STATE_ROOT",
"/var/lib/aios-release-preparation-supervisor-coordinator",
)
)
class CoordinatorError(RuntimeError):
def __init__(self, code: str) -> None:
super().__init__(code)
self.code = code
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def canonical(value: object) -> bytes:
return json.dumps(
value, sort_keys=True, separators=(",", ":"), ensure_ascii=False
).encode("utf-8")
def atomic_json(path: Path, value: Mapping[str, Any]) -> None:
path.parent.mkdir(parents=True, mode=0o750, exist_ok=True)
if path.parent.is_symlink() or path.is_symlink():
raise CoordinatorError("COORDINATOR_STATE_SYMLINK_REJECTED")
fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
os.fchmod(fd, 0o640)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(dict(value), handle, ensure_ascii=False, sort_keys=True)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def audit(event_type: str, **fields: Any) -> None:
record = {
"event_type": event_type,
"created_at": now(),
"agent_id": AGENT_ID,
"task_id": TASK_ID,
"credential_material_exposed": False,
"planner_credential_material_exposed": False,
"lease_material_exposed": False,
**fields,
}
STATE_ROOT.mkdir(parents=True, mode=0o750, exist_ok=True)
with (STATE_ROOT / "events.jsonl").open("a", encoding="utf-8") as handle:
handle.write(json.dumps(record, ensure_ascii=False, sort_keys=True) + "\n")
atomic_json(STATE_ROOT / "liveness.json", record)
def api_call(
method: str,
route: str,
token: str,
body: Mapping[str, Any] | None = None,
) -> tuple[int, dict[str, Any]]:
payload = None if body is None else canonical(dict(body))
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
if payload is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(
API_BASE + route, data=payload, headers=headers, method=method
)
try:
with urllib.request.urlopen(request, timeout=20) as response:
value = json.loads(response.read().decode("utf-8"))
return response.status, value if isinstance(value, dict) else {}
except urllib.error.HTTPError as exc:
try:
value = json.loads(exc.read(4096).decode("utf-8", "replace"))
except Exception:
value = {"error": "HTTP_ERROR"}
return exc.code, value if isinstance(value, dict) else {"error": "HTTP_ERROR"}
except (urllib.error.URLError, TimeoutError):
return 599, {"error": "AGENT_TASK_API_UNAVAILABLE"}
def load_agent_token() -> str:
path = Path(os.environ.get("CREDENTIALS_DIRECTORY", "")) / "agent_token"
if path.is_symlink() or not path.is_file():
raise CoordinatorError("COORDINATOR_AGENT_CREDENTIAL_UNAVAILABLE")
token = path.read_text(encoding="utf-8").strip()
if len(token) < 24 or any(character.isspace() for character in token):
raise CoordinatorError("COORDINATOR_AGENT_CREDENTIAL_INVALID")
return token
def validate_task(task: Mapping[str, Any]) -> dict[str, Any]:
if task.get("task_id") != TASK_ID or task.get("status") != "QUEUED":
raise CoordinatorError("SUPERVISOR_TASK_STATE_INVALID")
if task.get("required_capabilities") != REQUIRED_CAPABILITIES:
raise CoordinatorError("SUPERVISOR_COORDINATOR_CAPABILITIES_INVALID")
metadata = task.get("metadata")
if not isinstance(metadata, Mapping):
raise CoordinatorError("SUPERVISOR_EXECUTION_METADATA_MISSING")
execution = metadata.get("execution")
if not isinstance(execution, Mapping):
raise CoordinatorError("SUPERVISOR_EXECUTION_METADATA_MISSING")
governance = execution.get("governance")
if (
execution.get("action") != "agent_runtime"
or execution.get("adapter") != "openclaw"
or execution.get("capability_scope") != REQUIRED_CAPABILITIES
or execution.get("execution_digest") != EXPECTED_EXECUTION_DIGEST
or execution.get("change_set") != []
or execution.get("verification") != []
or not isinstance(governance, Mapping)
or governance.get("writer_lease") is not False
or any(
governance.get(field) is not False
for field in (
"main_write",
"production_write",
"tag_write",
"baseline_write",
"force_push",
"protected_ref_bypass",
)
)
):
raise CoordinatorError("SUPERVISOR_COORDINATOR_ENVELOPE_INVALID")
return dict(execution)
def heartbeat_progress(sequence: int) -> dict[str, Any]:
return {
"stage": "supervisor_coordinator_active",
"phase": "external_caller_projection",
"state": "PENDING_EXTERNAL_CONNECTOR_ACK",
"sequence": sequence,
"control_plane_access_granted": True,
"external_planner_role": "UNPRIVILEGED_PLANNER",
"credential_forwarded_to_planner": False,
"main_write": False,
"production_write": False,
"owner_approval_submitted": False,
}
def coordinate_claim(
claim: Mapping[str, Any],
token: str,
*,
max_heartbeats: int | None = None,
) -> int:
lease_token = claim.get("lease_token")
if not isinstance(lease_token, str) or len(lease_token) < 24:
raise CoordinatorError("COORDINATOR_LEASE_TOKEN_MISSING")
sequence = 0
while max_heartbeats is None or sequence < max_heartbeats:
sequence += 1
status, response = api_call(
"POST",
f"/v1/tasks/{TASK_ID}/heartbeat",
token,
{
"lease_token": lease_token,
"lease_seconds": LEASE_SECONDS,
"progress": heartbeat_progress(sequence),
},
)
if status != 200:
raise CoordinatorError(
"COORDINATOR_HEARTBEAT_FAILED_" + str(response.get("error") or status)
)
audit(
"SUPERVISOR_COORDINATOR_HEARTBEAT",
sequence=sequence,
task_status=response.get("status"),
lease_expires_at=response.get("lease_expires_at"),
control_plane_access_granted=True,
external_planner_role="UNPRIVILEGED_PLANNER",
)
if max_heartbeats is None or sequence < max_heartbeats:
time.sleep(HEARTBEAT_SECONDS)
return sequence
def worker_once(token: str) -> bool:
status, poll = api_call("POST", "/v1/tasks/poll", token, {"limit": 100})
if status != 200:
audit("SUPERVISOR_COORDINATOR_POLL_FAILED", error=poll.get("error") or status)
return False
task = next(
(
item
for item in poll.get("tasks", [])
if isinstance(item, Mapping) and item.get("task_id") == TASK_ID
),
None,
)
if task is None:
audit("SUPERVISOR_COORDINATOR_WAITING_FOR_TASK")
return False
validate_task(task)
status, claim = api_call(
"POST", f"/v1/tasks/{TASK_ID}/claim", token, {"lease_seconds": LEASE_SECONDS}
)
if status != 200:
audit("SUPERVISOR_COORDINATOR_CLAIM_FAILED", error=claim.get("error") or status)
return False
audit(
"SUPERVISOR_COORDINATOR_CLAIMED",
attempt=claim.get("attempt"),
revision=claim.get("revision"),
claim_mode="server_atomic",
control_plane_access_granted=True,
)
coordinate_claim(claim, token)
return True
def main() -> int:
if not EXPECTED_EXECUTION_DIGEST or len(EXPECTED_EXECUTION_DIGEST) != 64:
raise CoordinatorError("SUPERVISOR_EXECUTION_DIGEST_CONFIG_INVALID")
token = load_agent_token()
audit(
"SUPERVISOR_COORDINATOR_READY",
required_capabilities=REQUIRED_CAPABILITIES,
control_plane_access_granted=True,
external_planner_role="UNPRIVILEGED_PLANNER",
)
while True:
try:
worker_once(token)
except Exception as exc:
audit(
"SUPERVISOR_COORDINATOR_LOOP_ERROR",
error=exc.code if isinstance(exc, CoordinatorError) else type(exc).__name__,
)
time.sleep(POLL_SECONDS)
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,37 @@
[Unit]
Description=AIOS owner-controlled supervisor coordinator projection API
After=aios-agent-task-api.service aios-chatgpt-bridge-v2-staging.service
Wants=aios-agent-task-api.service aios-chatgpt-bridge-v2-staging.service
[Service]
Type=simple
User=aios-agent-task
Group=aios-agent-task
LoadCredential=agent_task_control_token:/etc/aios-gitops/agent-task-control.token
Environment=AIOS_AGENT_TASK_DB=/var/lib/aios-agent-task/state.sqlite3
Environment=AIOS_AGENT_TASK_CONTROL_TOKEN_FILE=%d/agent_task_control_token
Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892
Environment=AIOS_AGENT_TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZATION_ID=owner-supervisor-coordinator-projection-recovery-20260820
Environment=AIOS_SUPERVISOR_COORDINATOR_PROJECTION_HOST=127.0.0.1
Environment=AIOS_SUPERVISOR_COORDINATOR_PROJECTION_PORT=18944
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_agent_task_supervisor_coordinator_projection.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_UNIX AF_INET
ReadWritePaths=/var/lib/aios-agent-task
UMask=0077
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,45 @@
[Unit]
Description=AIOS credential-isolated release-preparation supervisor coordinator
After=network-online.target aios-agent-task-api.service aios-autonomous-agent-register.service
Wants=network-online.target aios-agent-task-api.service
Requires=aios-autonomous-agent-register.service
[Service]
Type=simple
User=aios-autonomous-agent-worker
Group=aios-autonomous-agent-worker
SupplementaryGroups=aios-agent-runtime
LoadCredentialEncrypted=agent_token:/etc/credstore.encrypted/aios-autonomous-agent-worker/agent_token
Environment=PYTHONUNBUFFERED=1
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_AGENT_ID=online-engineering-worker-cn-network-mkt-autonomous-20260815
Environment=AIOS_RELEASE_PREPARATION_SUPERVISOR_TASK_ID=r42-release-preparation-autonomous-supervisor-20260820
Environment=AIOS_SUPERVISOR_COORDINATOR_STATE_ROOT=/var/lib/aios-release-preparation-supervisor-coordinator
Environment=AIOS_SUPERVISOR_COORDINATOR_POLL_SECONDS=5
Environment=AIOS_SUPERVISOR_COORDINATOR_HEARTBEAT_SECONDS=10
Environment=AIOS_SUPERVISOR_COORDINATOR_LEASE_SECONDS=300
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_release_preparation_supervisor_coordinator.py
Restart=always
RestartSec=5
TimeoutStopSec=20
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictRealtime=true
RestrictNamespaces=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_UNIX
ReadWritePaths=/var/lib/aios-release-preparation-supervisor-coordinator
InaccessiblePaths=-/etc/credstore.encrypted
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
placeholder
@@ -10,9 +10,13 @@ ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime/app"))
from aios_capability_contract import ( # noqa: E402
capability_contract,
has_capabilities,
normalize_required_capabilities,
normalize_worker_capabilities,
normalized_capability_matrix,
registered_capability,
validate_capability_contract_registry,
)
@@ -31,3 +35,32 @@ def test_runtime_capabilities_are_isolated_between_workers() -> None:
def test_unknown_runtime_capability_remains_fail_closed() -> None:
assert has_capabilities(["agent.runtime.hermes"], ["agent.runtime.unknown"]) is False
def test_autonomous_coordinator_capability_is_registered_on_both_sides() -> None:
capability = "engineering.autonomous"
assert registered_capability(capability) is True
assert registered_capability(capability, worker=True) is True
assert normalize_required_capabilities([capability]) == frozenset({capability})
assert normalize_worker_capabilities([capability]) == frozenset({capability})
contract = capability_contract(capability)
assert contract is not None
assert contract.dependencies == ("agent_task_control_plane",)
assert validate_capability_contract_registry()["valid"] is True
def test_supervisor_coordinator_requires_runtime_and_autonomous_capabilities() -> None:
required = ["agent.runtime.openclaw", "engineering.autonomous"]
assert normalized_capability_matrix(required, required) == {
"worker_effective": required,
"task_required": required,
"eligible": True,
}
assert has_capabilities(["agent.runtime.openclaw"], required) is False
assert has_capabilities(["engineering.autonomous"], required) is False
def test_autonomous_capability_near_matches_remain_fail_closed() -> None:
worker = ["agent.runtime.openclaw", "engineering.autonomous"]
assert has_capabilities(worker, ["engineering.autonomous.extra"]) is False
assert has_capabilities(worker, ["engineering.*"]) is False
@@ -15,6 +15,7 @@ from aios_agent_task_initial_execution_projection import (
InitialExecutionProjectionError,
InitialExecutionProjectionRequest,
InitialExecutionProjectionStore,
SourceCaptureProjectionRecoveryRequest,
)
from aios_online_agent_execution import (
GovernedExecutionBinding,
@@ -166,6 +167,87 @@ def resolver(_task_id,_job_id,_capability,_adapter,received_runtime_input):
return envelope()
def source_capture_result():
old=envelope(action="source_capture",capability="git.read")
return {
"task_id":TASK_ID,
"job_id":JOB_ID,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":BRANCH,
"generation":1,
"status":"SUCCEEDED",
"classification":"NEEDS_REWORK_SOURCE_CAPTURE",
"execution_mode":"source_capture",
"execution_digest":old["execution_digest"],
"changed_files":[],
"writer_lease":"NONE",
"verification_state":"NEEDS_REWORK",
"direct_main_write":False,
"production_write":False,
"force_push":False,
"protected_ref_bypass":False,
"secret_material_exposed":False,
"source_snapshot":{"schema":"test.snapshot.v1","payload_sha256":"d"*64},
}
def make_source_capture_rework_db(tmp_path, *, mutate=None, events=None):
path=make_db(tmp_path)
result=source_capture_result()
verification={
"decision":"NEEDS_REWORK",
"reason":"SOURCE_CAPTURE_COMPLETE_RUNTIME_PROJECTION_REQUIRED",
}
if mutate:
mutate(result,verification)
event_types=events or [
"TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED","TASK_VERIFIED"
]
con=sqlite3.connect(path)
con.execute(
"UPDATE tasks SET attempt=1,status='QUEUED',claimed_by=NULL,revision=5,"
"result_json=?,result_digest=?,result_idempotency_key=?,verification_json=?",
(
json.dumps(result,sort_keys=True,separators=(",",":")),
canon(result),"source-capture-result-v1",
json.dumps(verification,sort_keys=True,separators=(",",":")),
),
)
for event_type in event_types:
payload={}
if event_type=="TASK_VERIFIED":
payload={"decision":"NEEDS_REWORK","next_state":"QUEUED"}
con.execute(
"INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) "
"VALUES (?,?,?,?,?)",
(TASK_ID,"generic-worker",event_type,json.dumps(payload),"2026-08-20T00:01:00Z"),
)
con.commit();con.close()
return path,result,verification
def recovery_request(result,verification,*,digest=None,key="source-capture-recovery-a-v1"):
return SourceCaptureProjectionRecoveryRequest.from_mapping({
"expected_status":"QUEUED",
"expected_revision":5,
"expected_attempt":1,
"expected_metadata_sha256":canon({}),
"expected_required_capabilities_sha256":canon([]),
"previous_verification_sha256":canon(verification),
"expected_source_capture_result_sha256":canon(result),
"expected_source_capture_execution_digest":result["execution_digest"],
"expected_job_id":JOB_ID,
"expected_execution_digest":digest or envelope()["execution_digest"],
"required_capability":CAPABILITY,
"adapter":"openclaw",
"authorization_id":AUTHORIZATION,
"idempotency_key":key,
"reason":"first durable runtime projection after governed source capture",
})
def test_initial_projection_cas_persists_envelope_capability_and_event(tmp_path):
path=make_db(tmp_path)
result=InitialExecutionProjectionStore(path,resolver).project(TASK_ID,request())
@@ -205,6 +287,110 @@ def test_initial_projection_idempotent_exact_replay(tmp_path):
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0]==2
def test_source_capture_recovery_projects_first_runtime_envelope(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
response=InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
assert response["revision"]==6
assert response["attempt"]==1
assert response["recovery_from_source_capture"] is True
assert response["idempotent"] is False
con=sqlite3.connect(path)
row=con.execute(
"SELECT status,attempt,revision,claimed_by,metadata_json,"
"required_capabilities_json,result_json,verification_json FROM tasks"
).fetchone()
assert row[:4]==("QUEUED",1,6,None)
metadata=json.loads(row[4])
assert metadata["execution"]["action"]=="agent_runtime"
assert metadata["execution"]["adapter"]=="openclaw"
assert metadata["execution"]["governance"]["writer_lease"] is False
assert json.loads(row[5])==[CAPABILITY]
assert json.loads(row[6])==result
assert json.loads(row[7])==verification
events=con.execute("SELECT event_type,payload_json FROM events ORDER BY event_id").fetchall()
assert [event[0] for event in events]==[
"TASK_CREATED","TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED",
"TASK_VERIFIED","TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZED",
]
payload=json.loads(events[-1][1])
assert payload["recovery_from_source_capture"] is True
assert payload["source_capture_execution_digest"]==result["execution_digest"]
assert payload["main_write"] is False
assert payload["production_write"] is False
def test_source_capture_recovery_idempotent_exact_replay(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
store=InitialExecutionProjectionStore(path,resolver)
first=store.project_from_source_capture(TASK_ID,recovery_request(result,verification))
second=store.project_from_source_capture(TASK_ID,recovery_request(result,verification))
assert first["idempotent"] is False
assert second["idempotent"] is True
assert second["execution_digest"]==first["execution_digest"]
con=sqlite3.connect(path)
assert con.execute(
"SELECT COUNT(*) FROM task_source_capture_projection_recoveries"
).fetchone()[0]==1
assert con.execute("SELECT COUNT(*) FROM events").fetchone()[0]==6
@pytest.mark.parametrize(
"mutate,error",
[
(lambda result,verification: result.__setitem__("changed_files",["x"]),
"SOURCE_CAPTURE_RESULT_CONTRACT_INVALID"),
(lambda result,verification: result.__setitem__("job_id","job-other"),
"SOURCE_CAPTURE_RESULT_CONTRACT_INVALID"),
(lambda result,verification: verification.__setitem__("decision","SUCCEEDED"),
"SOURCE_CAPTURE_NEEDS_REWORK_VERIFICATION_REQUIRED"),
],
)
def test_source_capture_recovery_rejects_unsafe_result_or_verification(
tmp_path,mutate,error
):
path,result,verification=make_source_capture_rework_db(tmp_path,mutate=mutate)
with pytest.raises(InitialExecutionProjectionError,match=error):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
@pytest.mark.parametrize(
"events",
[
["TASK_CLAIMED","RESULT_SUBMITTED","TASK_VERIFIED"],
["TASK_CLAIMED","TASK_HEARTBEAT","RESULT_SUBMITTED","TASK_VERIFIED","TASK_CLAIMED"],
["TASK_CLAIMED","TASK_HEARTBEAT","TASK_VERIFIED","RESULT_SUBMITTED"],
],
)
def test_source_capture_recovery_rejects_invalid_event_chain(tmp_path,events):
path,result,verification=make_source_capture_rework_db(tmp_path,events=events)
with pytest.raises(
InitialExecutionProjectionError,match="SOURCE_CAPTURE_RECOVERY_EVENT_CHAIN_INVALID"
):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification)
)
def test_source_capture_recovery_rejects_execution_digest_drift(tmp_path):
path,result,verification=make_source_capture_rework_db(tmp_path)
with pytest.raises(InitialExecutionProjectionError,match="EXECUTION_DIGEST_DRIFT"):
InitialExecutionProjectionStore(path,resolver).project_from_source_capture(
TASK_ID,recovery_request(result,verification,digest="f"*64)
)
def test_source_capture_recovery_request_is_strict(tmp_path):
_path,result,verification=make_source_capture_rework_db(tmp_path)
data=recovery_request(result,verification).public()
data["metadata_patch"]={"execution":"forbidden"}
with pytest.raises(InitialExecutionProjectionError,match="FIELDS_INVALID"):
SourceCaptureProjectionRecoveryRequest.from_mapping(data)
@pytest.mark.parametrize(
"kwargs,error",
[
@@ -0,0 +1,188 @@
from __future__ import annotations
from pathlib import Path
import hashlib
import json
import sqlite3
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_agent_task_initial_execution_projection import InitialExecutionProjectionError
from aios_agent_task_supervisor_coordinator_projection import (
COORDINATOR_CAPABILITIES,
RUNTIME_CAPABILITY,
SupervisorCoordinatorProjectionRequest,
SupervisorCoordinatorProjectionStore,
)
from aios_online_agent_execution import (
GovernedExecutionBinding,
project_execution_envelope,
)
TASK="task-supervisor-coordinator-a"
JOB="job-supervisor-coordinator-a"
BASE="a"*40
BRANCH="agent/task-supervisor-coordinator-a"
BINDING_KEY="task-supervisor-coordinator-binding-v1"
AUTH="owner-supervisor-coordinator-projection-recovery-20260820"
@pytest.fixture(autouse=True)
def authorization(monkeypatch):
monkeypatch.setenv(
"AIOS_AGENT_TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZATION_ID",AUTH
)
def sha(value):
return hashlib.sha256(
json.dumps(value,sort_keys=True,separators=(",",":")).encode()
).hexdigest()
def binding():
return GovernedExecutionBinding.from_mapping({
"task_id":TASK,"job_id":JOB,"repository":"aios/aios",
"base_oid":BASE,"expected_branch_oid":BASE,"target_branch":BRANCH,
"branch_mode":"CREATE_NEW","generation":1,"idempotency_key":BINDING_KEY,
})
def runtime_input():
return {
"goal":"Durably supervise release preparation",
"instructions":"Wait for exact connector ACK and preserve the same lineage.",
"success_criteria":[{"id":"heartbeat","description":"Coordinator heartbeat"}],
"constraints":{},
"scope":{"scope_type":"legacy","tenant_id":None,"project_id":None},
"trace_id":TASK,
}
def envelope(capabilities):
return project_execution_envelope(binding(),{
"action":"agent_runtime","adapter":"openclaw",
"capability_scope":capabilities,"runtime_input":runtime_input(),
})
def planner_result(old_digest,*,access=False):
return {
"task_id":TASK,"job_id":JOB,"repository":"aios/aios",
"base_oid":BASE,"expected_branch_oid":BASE,"target_branch":BRANCH,
"generation":1,"status":"SUCCEEDED","execution_mode":"agent_runtime",
"agent_runtime":"openclaw","execution_digest":old_digest,
"control_plane_access_granted_to_runtime":access,
"credential_material_exposed":False,"secret_material_exposed":False,
"changed_files":[],"writer_lease":"NONE","direct_main_write":False,
"production_write":False,"force_push":False,"protected_ref_bypass":False,
"classification":"EXTERNAL_AGENT_RUNTIME_SUCCEEDED",
}
def make_db(tmp_path,*,access=False,heartbeat=True):
tmp_path.mkdir(parents=True,exist_ok=True)
path=tmp_path/"state.sqlite3";con=sqlite3.connect(path)
con.executescript("""
CREATE TABLE tasks(
task_id TEXT PRIMARY KEY,goal TEXT NOT NULL,instructions TEXT NOT NULL,
success_criteria_json TEXT NOT NULL,required_capabilities_json TEXT NOT NULL,
constraints_json TEXT NOT NULL,metadata_json TEXT NOT NULL,deadline TEXT,
priority INTEGER NOT NULL,max_attempts INTEGER NOT NULL,attempt INTEGER NOT NULL,
status TEXT NOT NULL,claimed_by TEXT,lease_token_hash TEXT,lease_expires_at TEXT,
result_json TEXT,result_digest TEXT,result_idempotency_key TEXT,
verification_json TEXT,revision INTEGER NOT NULL,created_at TEXT,updated_at TEXT
);
CREATE TABLE events(
event_id INTEGER PRIMARY KEY AUTOINCREMENT,task_id TEXT,agent_id TEXT,
event_type TEXT NOT NULL,payload_json TEXT NOT NULL,created_at TEXT NOT NULL
);
""")
old=envelope([RUNTIME_CAPABILITY]);result=planner_result(old["execution_digest"],access=access)
verification={"decision":"NEEDS_REWORK","reason":"HOST_COORDINATOR_REQUIRED"}
metadata={"execution":old,"execution_projection":{
"schema":"aios.online-agent.task-projection.v1","task_id":TASK,
"job_id":JOB,"execution_digest":old["execution_digest"],
}}
con.execute("INSERT INTO tasks VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",(
TASK,runtime_input()["goal"],runtime_input()["instructions"],
json.dumps(runtime_input()["success_criteria"]),json.dumps([RUNTIME_CAPABILITY]),
"{}",json.dumps(metadata,sort_keys=True,separators=(",",":")),None,100,20,2,
"QUEUED",None,None,None,json.dumps(result,sort_keys=True,separators=(",",":")),
sha(result),"planner-result-v1",json.dumps(verification,sort_keys=True,separators=(",",":")),
11,"2026-08-20T00:00:00Z","2026-08-20T00:00:00Z",
))
events=["TASK_CREATED","TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZED","TASK_CLAIMED"]
if heartbeat: events.append("TASK_HEARTBEAT")
events += ["RESULT_SUBMITTED","TASK_VERIFIED"]
for event in events:
payload={"decision":"NEEDS_REWORK","next_state":"QUEUED"} if event=="TASK_VERIFIED" else {}
con.execute("INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) VALUES (?,?,?,?,?)",(TASK,"worker",event,json.dumps(payload),"2026-08-20T00:00:00Z"))
con.commit();con.close()
return path,metadata,result,verification,old
def request(metadata,result,verification,old,new):
return SupervisorCoordinatorProjectionRequest.from_mapping({
"expected_status":"QUEUED","expected_revision":11,"expected_attempt":2,
"expected_metadata_sha256":sha(metadata),
"expected_required_capabilities_sha256":sha([RUNTIME_CAPABILITY]),
"previous_verification_sha256":sha(verification),
"expected_planner_result_sha256":sha(result),
"expected_old_execution_digest":old["execution_digest"],
"expected_new_execution_digest":new["execution_digest"],
"expected_job_id":JOB,"authorization_id":AUTH,
"idempotency_key":"task-supervisor-coordinator-projection-v1",
"reason":"credential-isolated host coordinator required",
})
def test_projects_exact_coordinator_capabilities_and_preserves_planner_evidence(tmp_path):
path,metadata,result,verification,old=make_db(tmp_path)
new=envelope(COORDINATOR_CAPABILITIES)
store=SupervisorCoordinatorProjectionStore(path,lambda task,job,runtime:new)
response=store.project(TASK,request(metadata,result,verification,old,new))
assert response["revision"]==12 and response["attempt"]==2
assert response["required_capabilities"]==COORDINATOR_CAPABILITIES
assert response["credential_forwarded_to_planner"] is False
con=sqlite3.connect(path)
row=con.execute("select status,revision,metadata_json,required_capabilities_json,result_json from tasks").fetchone()
assert row[:2]==("QUEUED",12)
assert json.loads(row[2])["execution"]["execution_digest"]==new["execution_digest"]
assert json.loads(row[3])==COORDINATOR_CAPABILITIES
assert json.loads(row[4])==result
assert con.execute("select event_type from events order by event_id desc limit 1").fetchone()[0]=="TASK_SUPERVISOR_COORDINATOR_PROJECTION_AUTHORIZED"
def test_exact_replay_is_idempotent(tmp_path):
path,metadata,result,verification,old=make_db(tmp_path);new=envelope(COORDINATOR_CAPABILITIES)
store=SupervisorCoordinatorProjectionStore(path,lambda task,job,runtime:new)
req=request(metadata,result,verification,old,new)
assert store.project(TASK,req)["idempotent"] is False
assert store.project(TASK,req)["idempotent"] is True
def test_rejects_planner_with_control_access_or_missing_heartbeat(tmp_path):
path,metadata,result,verification,old=make_db(tmp_path,access=True);new=envelope(COORDINATOR_CAPABILITIES)
with pytest.raises(InitialExecutionProjectionError,match="RESULT_CONTRACT_INVALID"):
SupervisorCoordinatorProjectionStore(path,lambda *args:new).project(
TASK,request(metadata,result,verification,old,new)
)
path,metadata,result,verification,old=make_db(tmp_path/"second",heartbeat=False)
with pytest.raises(InitialExecutionProjectionError,match="EVENT_CHAIN_INVALID"):
SupervisorCoordinatorProjectionStore(path,lambda *args:new).project(
TASK,request(metadata,result,verification,old,new)
)
def test_request_rejects_arbitrary_metadata_patch(tmp_path):
path,metadata,result,verification,old=make_db(tmp_path);new=envelope(COORDINATOR_CAPABILITIES)
value=request(metadata,result,verification,old,new).public();value["metadata_patch"]={}
with pytest.raises(InitialExecutionProjectionError,match="FIELDS_INVALID"):
SupervisorCoordinatorProjectionRequest.from_mapping(value)
@@ -0,0 +1,112 @@
from pathlib import Path
import pytest
import yaml
from aios_chatgpt_bridge_v2 import OPERATION_REQUIRED_FIELDS
from aios_chatgpt_bridge_v2_server import BridgeHandler, _HTTPError, ROUTES
ROOT = Path(__file__).resolve().parents[1]
OPENAPI = ROOT / "docs" / "gitops" / "openapi-chatgpt-actions.yaml"
REQUIRED = {
"task_id",
"repository",
"base_oid",
"target_branch",
"risk",
"description",
"idempotency_key",
}
OPTIONAL_CONTINUATION = {
"expected_branch_oid",
"generation",
"parent_task_id",
"parent_job_id",
"priority",
}
def _document():
return yaml.safe_load(OPENAPI.read_text(encoding="utf-8"))
def _create_request():
return {
"task_id": "continuation-contract-test",
"repository": "aios/aios",
"base_oid": "a" * 40,
"target_branch": "agent/continuation-contract-test",
"risk": "engineering",
"description": "contract test",
"idempotency_key": "continuation-contract-test-v1",
}
def test_create_governed_task_openapi_contract_exposes_continuation_fields():
document = _document()
operation = document["paths"]["/createGovernedTask"]["post"]
schema_ref = operation["requestBody"]["content"]["application/json"]["schema"]
schema = document["components"]["schemas"][schema_ref["$ref"].rsplit("/", 1)[-1]]
assert operation["operationId"] == "createGovernedTask"
assert operation["security"] == [{"bearerAuth": []}]
assert operation["x-openai-isConsequential"] is True
assert set(schema["required"]) == REQUIRED
assert schema["additionalProperties"] is False
assert OPTIONAL_CONTINUATION <= set(schema["properties"])
assert not OPTIONAL_CONTINUATION.intersection(schema["required"])
assert schema["properties"]["repository"]["const"] == "aios/aios"
assert schema["properties"]["generation"]["minimum"] == 1
assert schema["properties"]["priority"] == {
"type": "integer",
"minimum": -100,
"maximum": 100,
}
def test_create_governed_task_route_and_required_fields_match_bridge():
assert ROUTES["/v2/governed-task"] == "createGovernedTask"
assert tuple(OPERATION_REQUIRED_FIELDS["createGovernedTask"]) == (
"task_id",
"repository",
"base_oid",
"target_branch",
"risk",
"description",
"idempotency_key",
)
def test_bridge_execution_plan_and_envelope_routes_remain_exposed():
assert ROUTES["/v2/execution-envelope"] == "projectOnlineAgentExecution"
assert ROUTES["/v2/execution-plan"] == "registerOnlineAgentExecutionPlan"
def test_bridge_allowlist_accepts_parent_pair_and_priority_but_rejects_unknown():
handler = BridgeHandler.__new__(BridgeHandler)
body = _create_request()
body.update(
{
"expected_branch_oid": "a" * 40,
"generation": 2,
"parent_task_id": "continuation-parent-test",
"parent_job_id": "job-continuation-parent-test",
"priority": 100,
}
)
handler._allowed("createGovernedTask", body)
invalid = dict(body)
invalid["unexpected_field"] = True
with pytest.raises(_HTTPError, match="UNKNOWN_FIELDS"):
handler._allowed("createGovernedTask", invalid)
def test_openapi_does_not_add_credential_fields_to_create_contract():
document = _document()
properties = set(
document["components"]["schemas"]["CreateGovernedTaskRequest"]["properties"]
)
forbidden = {"authorization", "bearer", "token", "credential", "secret", "password", "api_key"}
assert properties.isdisjoint(forbidden)
@@ -0,0 +1,116 @@
from __future__ import annotations
from pathlib import Path
import json
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
import aios_release_preparation_supervisor_coordinator as coordinator
def task():
return {
"task_id":coordinator.TASK_ID,
"status":"QUEUED",
"required_capabilities":coordinator.REQUIRED_CAPABILITIES,
"metadata":{
"execution":{
"action":"agent_runtime",
"adapter":"openclaw",
"capability_scope":coordinator.REQUIRED_CAPABILITIES,
"execution_digest":"e"*64,
"change_set":[],
"verification":[],
"governance":{
"writer_lease":False,
"main_write":False,
"production_write":False,
"tag_write":False,
"baseline_write":False,
"force_push":False,
"protected_ref_bypass":False,
},
}
},
}
@pytest.fixture(autouse=True)
def expected_digest(monkeypatch):
monkeypatch.setattr(coordinator,"EXPECTED_EXECUTION_DIGEST","e"*64)
def test_coordinator_accepts_only_exact_nonmutating_projection():
execution=coordinator.validate_task(task())
assert execution["adapter"]=="openclaw"
assert execution["governance"]["writer_lease"] is False
@pytest.mark.parametrize(
"mutate,error",
[
(lambda value:value.__setitem__("required_capabilities",["agent.runtime.openclaw"]),
"CAPABILITIES_INVALID"),
(lambda value:value["metadata"]["execution"].__setitem__("execution_digest","f"*64),
"ENVELOPE_INVALID"),
(lambda value:value["metadata"]["execution"]["governance"].__setitem__("production_write",True),
"ENVELOPE_INVALID"),
],
)
def test_coordinator_rejects_capability_or_governance_drift(mutate,error):
value=task();mutate(value)
with pytest.raises(coordinator.CoordinatorError,match=error):
coordinator.validate_task(value)
def test_heartbeat_is_control_plane_host_only_and_never_forwards_credentials():
progress=coordinator.heartbeat_progress(7)
assert progress["control_plane_access_granted"] is True
assert progress["external_planner_role"]=="UNPRIVILEGED_PLANNER"
assert progress["credential_forwarded_to_planner"] is False
assert progress["main_write"] is False
assert progress["production_write"] is False
encoded=json.dumps(progress,sort_keys=True).lower()
assert "bearer " not in encoded
assert "lease_token" not in encoded
def test_coordinate_claim_heartbeats_without_submitting_result(monkeypatch,tmp_path):
calls=[]
def fake(method,route,token,body=None):
calls.append((method,route,token,body))
return 200,{"status":"RUNNING","lease_expires_at":"2099-01-01T00:00:00Z"}
monkeypatch.setattr(coordinator,"api_call",fake)
monkeypatch.setattr(coordinator,"STATE_ROOT",tmp_path)
monkeypatch.setattr(coordinator,"HEARTBEAT_SECONDS",0)
count=coordinator.coordinate_claim(
{"lease_token":"l"*32},"agent-token-value-for-test",max_heartbeats=2
)
assert count==2
assert all(call[1].endswith("/heartbeat") for call in calls)
assert not any(call[1].endswith("/result") for call in calls)
assert all("agent-token" not in json.dumps(call[3]) for call in calls)
liveness=json.loads((tmp_path/"liveness.json").read_text())
assert liveness["event_type"]=="SUPERVISOR_COORDINATOR_HEARTBEAT"
assert liveness["credential_material_exposed"] is False
def test_units_use_existing_encrypted_agent_credential_and_no_planner_credential():
coordinator_unit=(
ROOT/"deploy/systemd/aios-release-preparation-supervisor-coordinator.service"
).read_text()
projection_unit=(
ROOT/"deploy/systemd/aios-agent-task-supervisor-coordinator-projection.service"
).read_text()
assert "aios-autonomous-agent-register.service" in coordinator_unit
assert "LoadCredentialEncrypted=agent_token:" in coordinator_unit
assert "control_token" not in coordinator_unit
assert "SupplementaryGroups=aios-agent-runtime" in coordinator_unit
assert "LoadCredential=agent_task_control_token:" in projection_unit
assert "User=aios-agent-task" in projection_unit
assert "ReadWritePaths=/var/lib/aios-agent-task" in projection_unit
+215
View File
@@ -0,0 +1,215 @@
import ast
import json
import os
import struct
from collections.abc import Mapping
from pathlib import Path
from types import SimpleNamespace
from typing import Any
import pytest
from aios_agent_task_protocol import TaskProtocolError, TaskStore
from aios_chatgpt_bridge_v2_server import BridgeHandler
import aios_durable_owner_authorization as owner_auth
from aios_online_agent_execution import GovernedExecutionBinding, project_online_task
from aios_online_agent_worker import WorkerError, WorkerSupervisor
CAPABILITIES = ["git.read", "git.test", "governed.write.branch", "governed.write.file"]
def binding(task_id="unified-test-task-20260824"):
return GovernedExecutionBinding.from_mapping({
"task_id": task_id,
"job_id": "job-" + "a" * 32,
"repository": "aios/aios",
"base_oid": "0" * 40,
"expected_branch_oid": "0" * 40,
"target_branch": "agent/unified-test-20260824",
"branch_mode": "CREATE_NEW",
"generation": 1,
"idempotency_key": "unified-test-task-20260824-v1",
})
def projected(task_id="unified-test-task-20260824"):
return project_online_task(
binding(task_id),
{
"goal": "test governed task",
"instructions": "test only",
"success_criteria": [],
"required_capabilities": CAPABILITIES,
"priority": 50,
"max_attempts": 3,
},
{
"action": "source_capture",
"adapter": "source_capture",
"capability_scope": CAPABILITIES,
"source_signals": ["durable_policy"],
},
)
def test_write_task_rejected_before_persistence(tmp_path):
store = TaskStore(tmp_path / "state.sqlite3")
with pytest.raises(TaskProtocolError) as error:
store.create_task(
task_id="bare-write-task-20260824",
goal="bare",
instructions="must reject",
success_criteria=[],
required_capabilities=CAPABILITIES,
metadata={},
idempotency_key="bare-write-task-20260824-v1",
)
assert error.value.code == "GOVERNED_EXECUTION_BINDING_REQUIRED"
with pytest.raises(TaskProtocolError) as missing:
store.get_task("bare-write-task-20260824")
assert missing.value.code == "TASK_NOT_FOUND"
def test_complete_projection_persists_envelope(tmp_path):
store = TaskStore(tmp_path / "state.sqlite3")
task = projected()
created = store.create_task(
task_id=task["task_id"],
goal=task["goal"],
instructions=task["instructions"],
success_criteria=task["success_criteria"],
required_capabilities=task["required_capabilities"],
constraints={},
metadata=task["metadata"],
priority=task["priority"],
max_attempts=task["max_attempts"],
idempotency_key=task["idempotency_key"],
)
execution = created["metadata"]["execution"]
assert created["status"] == "QUEUED"
assert execution["schema"] == "aios.online-agent.execution.v2"
assert execution["job_id"].startswith("job-")
assert execution["execution_digest"]
assert created["metadata"]["execution_projection"]["execution_digest"] == execution["execution_digest"]
def test_online_route_requires_governed_creation_for_write_caps():
source = Path(__file__).parents[1] / "active-runtime" / "app" / "aios_model_api_staging.py"
tree = ast.parse(source.read_text(encoding="utf-8"))
function = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "requires_governed_execution")
namespace = {
"Mapping": Mapping,
"Any": Any,
"GOVERNED_EXECUTION_CAPABILITIES": {"governed.write.branch", "governed.write.file"},
}
exec(compile(ast.Module(body=[function], type_ignores=[]), str(source), "exec"), namespace)
guard = namespace["requires_governed_execution"]
assert guard({"required_capabilities": CAPABILITIES})
assert not guard({"required_capabilities": ["git.read", "git.test"]})
def test_bridge_requires_claim_for_projection_and_job(monkeypatch):
handler = object.__new__(BridgeHandler)
handler.headers = {"Authorization": "Bearer agent-credential"}
handler.control_token = None
handler.agent_task_api_url = "http://task-api"
class Response:
def __init__(self, value):
self.value = value
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self):
return json.dumps(self.value).encode()
responses = iter([
{"agent_id": "worker-1"},
{"task_id": "task-1", "claimed_by": None, "status": "QUEUED"},
])
monkeypatch.setattr("aios_chatgpt_bridge_v2_server.urllib.request.urlopen", lambda *_args, **_kwargs: Response(next(responses)))
assert not handler._authorized("projectOnlineAgentExecution", {"task_id": "task-1"})
responses = iter([
{"agent_id": "worker-1"},
{"task_id": "task-1", "claimed_by": "worker-1", "status": "CLAIMED"},
])
assert handler._authorized("getGitOpsJob", {"task_id": "task-1"})
def test_broker_uses_so_peercred_and_canonical_group(monkeypatch):
class Connection:
def getsockopt(self, *_args):
return struct.pack("3i", os.getpid(), os.getuid(), os.getgid())
monkeypatch.setattr(owner_auth, "_peer_process_status", lambda _pid: (os.getuid(), os.getgid(), {os.getgid(), 4242}))
monkeypatch.setattr(owner_auth, "CANONICAL_BROKER_GROUP", "aios-autonomous-agent-worker")
monkeypatch.setattr(owner_auth.grp, "getgrnam", lambda _name: SimpleNamespace(gr_gid=4242))
assert owner_auth.authorize_broker_peer(Connection())
monkeypatch.setattr(owner_auth, "_peer_process_status", lambda _pid: (os.getuid(), os.getgid(), {os.getgid()}))
with pytest.raises(owner_auth.AuthorizationError) as error:
owner_auth.authorize_broker_peer(Connection())
assert error.value.code == "SOCKGROUP_NOT_AUTHORIZED"
def test_bootstrap_rejects_bad_projection_before_job(tmp_path):
from aios_governed_task_bootstrap import BootstrapError, GovernedTaskBootstrap
class Authority:
def commit_exists(self, _oid):
return True
def read_ref(self, _ref):
return None
bootstrap = GovernedTaskBootstrap(
Authority(), tmp_path, remote_name="gitea-http",
token_path=tmp_path / "token", ssh_key_path=tmp_path / "key",
)
with pytest.raises(BootstrapError) as error:
bootstrap.create({
"task_id": "atomic-projection-failure-20260824",
"repository": "aios/aios",
"base_oid": "0" * 40,
"expected_branch_oid": "0" * 40,
"target_branch": "agent/atomic-projection-failure-20260824",
"risk": "engineering",
"description": "invalid deterministic placeholder",
"idempotency_key": "atomic-projection-failure-20260824-v1",
"online_task": {
"goal": "invalid",
"instructions": "invalid",
"success_criteria": [],
"required_capabilities": CAPABILITIES,
},
"execution": {
"action": "deterministic",
"adapter": "deterministic",
"capability_scope": CAPABILITIES,
"change_set": [],
"verification": [],
},
"require_online_task_projection": True,
})
assert error.value.args[0] == "EXECUTION_BINDING_INVALID"
assert not list((tmp_path / "jobs").rglob("state.sqlite3"))
assert not list((tmp_path / "task-bindings").glob("*.json"))
def test_worker_rejects_bare_write_task():
supervisor = object.__new__(WorkerSupervisor)
with pytest.raises(WorkerError) as error:
supervisor.resolve_envelope({
"task_id": "bare-worker-task-20260824",
"required_capabilities": CAPABILITIES,
"metadata": {},
})
assert error.value.code == "TASK_SKIPPED_PROJECTION_MISSING"
def test_continuation_scope_allows_existing_subset():
source = Path(__file__).parents[1] / "active-runtime" / "app" / "aios_continuation_executor.py"
text = source.read_text(encoding="utf-8")
assert "if dirty and not dirty.issubset(expected_paths):" in text
assert "if dirty and dirty != expected_paths:" not in text