Compare commits

...
Author SHA1 Message Date
AIOS Governed Continuation ee03089bcf chore: repair GPT Actions OpenAPI projection to 27 operations 2026-08-25 09:47:21 +08:00
AIOS Governed Continuation 341d276f18 feat(gitops): expose governed task continuation contract 2026-08-24 21:15:07 +08:00
AIOS Governed Continuation 2c8c98a11f probe 2026-08-24 16:08:34 +08:00
AIOS Governed Continuation bed7dd7f03 fix(gitops): unify governed execution admission 2026-08-24 12:52:40 +08:00
AIOS Governed Continuation 020d1ecf73 fix(task-plane): admit autonomous supervisor coordinator 2026-08-20 10:50:40 +08:00
AIOS Governed Continuation 7d924b5c18 feat(supervisor): add credential-isolated coordinator 2026-08-20 10:32:24 +08:00
AIOS Governed Continuation ad20eeb5c6 fix(agent-task): recover runtime projection after source capture 2026-08-20 10:17:11 +08:00
AIOS Governed Continuation 0ee456da94 feat(agent-task): add initial execution projection recovery 2026-08-20 10:00:44 +08:00
AIOS Governed Continuation d957a216a6 fix(release): separate immutable V2R2C2 lineage from engineering lineage 2026-08-19 23:00:07 +08:00
AIOS Governed Continuation b4b4416c93 feat(control-plane): add admission invariant repair core 2026-08-19 22:28:38 +08:00
AIOS Governed Continuation 17deb91ca7 fix(task-api): normalize release candidate capability 2026-08-19 18:06:41 +08:00
AIOS Governed Continuation 71e736b05f feat(release): add dedicated release preparation worker 2026-08-19 17:13:20 +08:00
AIOS Governed Continuation 5703ed7ae0 feat(governance): persist external release capability projection outbox 2026-08-19 14:21:05 +08:00
AIOS Governed Continuation 57218959b0 feat(governance): add durable Owner authorization projection 2026-08-19 13:49:45 +08:00
AIOS Governed Continuation 867d4a5173 fix(evidence): make all-modified-files scanner PEM-header precise 2026-08-19 11:18:23 +08:00
AIOS Governed Continuation 3a02a25d79 feat(gpt-edge): add owner-approved activation and rollback controller 2026-08-19 10:34:20 +08:00
AIOS Governed Continuation 69a51d50c7 feat(gpt-edge): add disabled-by-default TLS edge 2026-08-19 09:48:00 +08:00
AIOS Governed Continuation 7f12094e58 feat(online-task): add credential-isolated external GPT read-only ingress 2026-08-19 09:20:52 +08:00
AIOS Governed Continuation 2ae50a2a3d fix(model-governance): persist policy floor source of truth 2026-08-19 01:44:04 +08:00
AIOS Governed Continuation e9426dc0b8 fix(model-governance): reserve OpenClaw injected context 2026-08-18 20:02:52 +08:00
AIOS Governed Continuation d31267e89c fix(external-agent): scope request identity by task attempt 2026-08-18 17:01:38 +08:00
AIOS Governed Continuation f4022d3d1e feat(model-governance): enforce routed budgets and runtime policy 2026-08-18 13:18:20 +08:00
AIOS Governed Continuation 008bea3324 feat(online-agent): persist workers and defer engineering placeholders 2026-08-18 12:24:42 +08:00
AIOS Governed Continuation 52ef41e45f fix(online-agent): persist external runtime completion checkpoint phase 2026-08-18 08:46:05 +08:00
AIOS Governed Continuation 416e5e295d fix(online-agent): replay persisted external runtime envelope through canonical projection 2026-08-18 07:59:36 +08:00
AIOS Governed Continuation 4f81344509 fix(online-agent): normalize external runtime capabilities 2026-08-18 07:31:27 +08:00
83 changed files with 21174 additions and 749 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()
File diff suppressed because it is too large Load Diff
@@ -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,470 @@
#!/usr/bin/env python3
"""Canonical AIOS all-modified-files evidence packager.
The bundle contract is intentionally compatible with the historical
aios_package_all_modified_files_v1.sh output while keeping secret scanning in
version-controlled, tested code.
"""
from __future__ import annotations
from datetime import datetime, timezone
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import subprocess
import tempfile
import zipfile
from typing import Iterable
OID_RE = re.compile(r"^[0-9a-f]{40}$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
# A PEM private-key finding requires the complete PEM header. Source code,
# tests, docs, or error text containing only "BEGIN PRIVATE KEY" are not
# credentials and must not be rejected.
PEM_PRIVATE_KEY_RE = re.compile(
rb"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
re.IGNORECASE,
)
BEARER_RE = re.compile(
rb"authorization\s*:\s*bearer\s+[A-Za-z0-9._~+/=-]{20,}",
re.IGNORECASE,
)
KEY_VALUE_SECRET_RE = re.compile(
rb"(?:password|passwd|secret|api[_-]?key)\s*[:=]\s*[^\s,;]{12,}",
re.IGNORECASE,
)
DOCUMENT_SUFFIXES = {".md", ".rst", ".adoc", ".txt"}
DOCUMENT_PREFIXES = (
"README",
"CHANGELOG",
"RUNBOOK",
"ADR",
"ARCHITECTURE",
"DEPLOYMENT",
"TROUBLESHOOTING",
)
class PackagerError(RuntimeError):
pass
def sha256(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def secret_finding(data: bytes) -> str | None:
if PEM_PRIVATE_KEY_RE.search(data):
return "PEM_PRIVATE_KEY"
if BEARER_RE.search(data):
return "AUTHORIZATION_BEARER"
if KEY_VALUE_SECRET_RE.search(data):
return "KEY_VALUE_SECRET"
return None
def scan_secret(data: bytes, label: str) -> None:
finding = secret_finding(data)
if finding is not None:
raise PackagerError(
f"MODIFIED_FILES_SECRET_SCAN_MATCH:{finding}:{label}"
)
def git_bytes(git_dir: Path, oid: str, path: str) -> bytes:
try:
return subprocess.check_output(
[
"/usr/bin/git",
f"--git-dir={git_dir}",
"show",
f"{oid}:{path}",
],
timeout=30,
)
except subprocess.CalledProcessError as exc:
raise PackagerError(
"MODIFIED_FILES_GIT_SHOW_FAILED:" + path
) from exc
def git_text(git_dir: Path, *args: str) -> str:
try:
return subprocess.check_output(
["/usr/bin/git", f"--git-dir={git_dir}", *args],
text=True,
timeout=30,
)
except subprocess.CalledProcessError as exc:
raise PackagerError(
"MODIFIED_FILES_GIT_COMMAND_FAILED:" + " ".join(args)
) from exc
def validate_lineage(git_dir: Path, base: str, candidate: str) -> None:
if OID_RE.fullmatch(base) is None or OID_RE.fullmatch(candidate) is None:
raise PackagerError("MODIFIED_FILES_OID_INVALID")
for oid in (base, candidate):
try:
subprocess.run(
[
"/usr/bin/git",
f"--git-dir={git_dir}",
"cat-file",
"-e",
f"{oid}^{{commit}}",
],
check=True,
timeout=30,
)
except subprocess.CalledProcessError as exc:
raise PackagerError("MODIFIED_FILES_COMMIT_MISSING:" + oid) from exc
if subprocess.run(
[
"/usr/bin/git",
f"--git-dir={git_dir}",
"merge-base",
"--is-ancestor",
base,
candidate,
],
check=False,
timeout=30,
).returncode != 0:
raise PackagerError("MODIFIED_FILES_LINEAGE_INVALID")
def safe_destination(root: Path, relative: str) -> Path:
path = Path(relative)
if path.is_absolute() or ".." in path.parts:
raise PackagerError("MODIFIED_FILE_PATH_INVALID:" + relative)
destination = root / path
destination.parent.mkdir(parents=True, exist_ok=True)
return destination
def is_document_path(path: str) -> bool:
value = Path(path)
if path.startswith("docs/"):
return True
if value.suffix.lower() in DOCUMENT_SUFFIXES:
return True
upper = value.name.upper()
return any(upper.startswith(prefix) for prefix in DOCUMENT_PREFIXES)
def diff_entries(
git_dir: Path,
base: str,
candidate: str,
before_root: Path,
after_root: Path,
) -> list[dict[str, object]]:
raw = git_text(
git_dir,
"diff",
"--name-status",
"-M",
"-C",
base,
candidate,
)
entries: list[dict[str, object]] = []
for line in raw.splitlines():
if not line.strip():
continue
parts = line.split("\t")
status = parts[0]
code = status[0]
old_path: str | None = None
new_path: str | None = None
if code in {"R", "C"}:
if len(parts) != 3:
raise PackagerError(
"MODIFIED_FILES_DIFF_PARSE_INVALID:" + line
)
old_path, new_path = parts[1], parts[2]
else:
if len(parts) != 2:
raise PackagerError(
"MODIFIED_FILES_DIFF_PARSE_INVALID:" + line
)
new_path = parts[1] if code != "D" else None
old_path = parts[1] if code != "A" else None
entry: dict[str, object] = {"status": status}
if old_path is not None:
entry["previous_path"] = old_path
before = git_bytes(git_dir, base, old_path)
scan_secret(before, "before:" + old_path)
safe_destination(before_root, old_path).write_bytes(before)
entry["before_sha256"] = sha256(before)
entry["before_bytes"] = len(before)
if new_path is not None:
entry["path"] = new_path
after = git_bytes(git_dir, candidate, new_path)
scan_secret(after, "after:" + new_path)
safe_destination(after_root, new_path).write_bytes(after)
entry["after_sha256"] = sha256(after)
entry["after_bytes"] = len(after)
entry["deleted"] = code == "D"
entry["added"] = code == "A"
entry["renamed"] = code == "R"
entry["copied"] = code == "C"
entries.append(entry)
return entries
def execution_entries(
extra_root: Path | None,
execution_root: Path,
) -> list[dict[str, object]]:
if extra_root is None or not extra_root.exists():
return []
if extra_root.is_symlink() or not extra_root.is_dir():
raise PackagerError("EXTRA_FILES_DIR_INVALID")
entries: list[dict[str, object]] = []
for path in sorted(extra_root.rglob("*")):
if path.is_symlink():
raise PackagerError(
"EXTRA_FILE_SYMLINK_REJECTED:" + str(path)
)
if not path.is_file():
continue
relative = path.relative_to(extra_root).as_posix()
data = path.read_bytes()
scan_secret(data, "execution:" + relative)
safe_destination(execution_root, relative).write_bytes(data)
entries.append(
{
"path": relative,
"sha256": sha256(data),
"bytes": len(data),
}
)
return entries
def write_checksums(bundle: Path) -> None:
checks: list[tuple[str, str]] = []
for path in sorted(bundle.rglob("*")):
if path.is_file() and path.name != "checksums.sha256":
checks.append(
(
sha256(path.read_bytes()),
path.relative_to(bundle).as_posix(),
)
)
(bundle / "checksums.sha256").write_text(
"".join(f"{digest} {name}\n" for digest, name in checks),
encoding="utf-8",
)
def verify_checksums(bundle: Path) -> None:
checksum_path = bundle / "checksums.sha256"
for line in checksum_path.read_text(encoding="utf-8").splitlines():
digest, relative = line.split(" ", 1)
path = bundle / relative
if not path.is_file() or sha256(path.read_bytes()) != digest:
raise PackagerError(
"MODIFIED_FILES_CHECKSUM_CLOSURE_INVALID:" + relative
)
def build_bundle(
*,
git_dir: Path,
base: str,
candidate: str,
task_id: str,
extra_dir: Path | None = None,
output_root: Path | None = None,
now: datetime | None = None,
) -> dict[str, object]:
if TASK_RE.fullmatch(task_id) is None:
raise PackagerError("TASK_ID_INVALID")
validate_lineage(git_dir, base, candidate)
timestamp = (now or datetime.now(timezone.utc)).strftime(
"%Y%m%dT%H%M%SZ"
)
safe_task = re.sub(r"[^A-Za-z0-9._-]+", "-", task_id).strip("-")
if output_root is None:
output_root = Path(
tempfile.mkdtemp(
prefix=f"aios-modified-files-bundle-{safe_task}-{timestamp}-",
dir="/var/tmp",
)
)
else:
output_root.mkdir(parents=True, exist_ok=True)
bundle = output_root / "bundle"
before_root = bundle / "repo" / "before"
after_root = bundle / "repo" / "after"
execution_root = bundle / "execution"
before_root.mkdir(parents=True)
after_root.mkdir(parents=True)
execution_root.mkdir(parents=True)
changes = diff_entries(
git_dir,
base,
candidate,
before_root,
after_root,
)
execution_files = execution_entries(extra_dir, execution_root)
document_changes = [
entry
for entry in changes
if is_document_path(
str(entry.get("path") or entry.get("previous_path") or "")
)
]
execution_documents = [
entry
for entry in execution_files
if is_document_path(str(entry["path"]))
]
documents_index = {
"schema": "aios.modified-documents.index.v1",
"task_id": task_id,
"base_oid": base,
"candidate_oid": candidate,
"repository_documents": document_changes,
"execution_documents": execution_documents,
"repository_document_count": len(document_changes),
"execution_document_count": len(execution_documents),
}
(bundle / "documents-index.json").write_text(
json.dumps(documents_index, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
manifest = {
"schema": "aios.all-modified-files.bundle.v1",
"task_id": task_id,
"base_oid": base,
"candidate_oid": candidate,
"generated_at": (
now or datetime.now(timezone.utc)
).isoformat(),
"changed_file_count": len(changes),
"execution_file_count": len(execution_files),
"repository_document_count": len(document_changes),
"execution_document_count": len(execution_documents),
"changes": changes,
"execution_files": execution_files,
}
(bundle / "all-modified-files-manifest.json").write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
write_checksums(bundle)
verify_checksums(bundle)
zip_name = (
f"AIOS_{task_id}_ALL_MODIFIED_FILES_{candidate[:12]}.zip"
.replace("/", "-")
)
zip_path = output_root / zip_name
with zipfile.ZipFile(
zip_path,
"w",
compression=zipfile.ZIP_DEFLATED,
) as archive:
for path in sorted(bundle.rglob("*")):
if path.is_symlink():
raise PackagerError(
"BUNDLE_SYMLINK_REJECTED:" + str(path)
)
if path.is_file():
archive.write(
path,
arcname=path.relative_to(bundle).as_posix(),
)
zip_sha = sha256(zip_path.read_bytes())
summary = {
"schema": "aios.all-modified-files.bundle-summary.v1",
"task_id": task_id,
"base_oid": base,
"candidate_oid": candidate,
"bundle_path": str(zip_path),
"bundle_sha256": zip_sha,
"changed_file_count": len(changes),
"execution_file_count": len(execution_files),
"repository_document_count": len(document_changes),
"execution_document_count": len(execution_documents),
}
(output_root / "ALL_MODIFIED_FILES_BUNDLE.json").write_text(
json.dumps(summary, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
return summary
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("base")
parser.add_argument("candidate")
parser.add_argument("task_id")
parser.add_argument("extra_dir", nargs="?")
parser.add_argument(
"--authority-git-dir",
default=os.environ.get(
"AIOS_AUTHORITY_GIT_DIR",
"/data/aios-gitops/repo.git",
),
)
parser.add_argument("--output-root")
args = parser.parse_args()
try:
summary = build_bundle(
git_dir=Path(args.authority_git_dir),
base=args.base,
candidate=args.candidate,
task_id=args.task_id,
extra_dir=Path(args.extra_dir) if args.extra_dir else None,
output_root=(
Path(args.output_root) if args.output_root else None
),
)
except PackagerError as exc:
print("FATAL:" + str(exc))
return 20
print("ALL_MODIFIED_FILES_BUNDLE=PASS")
print("ALL_MODIFIED_FILES_BUNDLE_PATH=" + str(summary["bundle_path"]))
print("ALL_MODIFIED_FILES_BUNDLE_SHA256=" + str(summary["bundle_sha256"]))
print("CHANGED_FILE_COUNT=" + str(summary["changed_file_count"]))
print("EXECUTION_FILE_COUNT=" + str(summary["execution_file_count"]))
print(
"REPOSITORY_DOCUMENT_COUNT="
+ str(summary["repository_document_count"])
)
print(
"EXECUTION_DOCUMENT_COUNT="
+ str(summary["execution_document_count"])
)
print(
"ALL_MODIFIED_FILES_EVIDENCE="
+ str(Path(str(summary["bundle_path"])).parent)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,288 @@
"""Unified autonomous control-plane primitives.
This is deliberately a library, not another daemon. Task API, workers and host
recovery code consume the same Admission -> Invariant -> Repair semantics.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
from enum import Enum
import re
from typing import Callable, Iterable, Mapping, Sequence
from aios_capability_contract import (
capability_contract,
has_capabilities,
normalize_required_capabilities,
normalize_worker_capabilities,
registered_capability,
validate_capability_contract_registry,
)
class FailureClass(str, Enum):
CAPABILITY_CONTRACT_ERROR = "CAPABILITY_CONTRACT_ERROR"
CREDENTIAL_PROJECTION_ERROR = "CREDENTIAL_PROJECTION_ERROR"
DEPENDENCY_UNAVAILABLE = "DEPENDENCY_UNAVAILABLE"
POLICY_DENIED = "POLICY_DENIED"
RESOURCE_UNAVAILABLE = "RESOURCE_UNAVAILABLE"
WORKER_CRASH = "WORKER_CRASH"
LEASE_LOST = "LEASE_LOST"
BUSINESS_PERMANENT = "BUSINESS_PERMANENT"
UNKNOWN = "UNKNOWN"
class RepairLevel(int, Enum):
AUTO_LOCAL = 0
AUTO_REPAIR_TASK = 1
OWNER_APPROVAL = 2
MANUAL = 3
@dataclass(frozen=True)
class InvariantResult:
name: str
passed: bool
detail: str = ""
def as_dict(self) -> dict[str, object]:
return asdict(self)
@dataclass(frozen=True)
class AdmissionDecision:
admitted: bool
state: str
invariants: tuple[InvariantResult, ...]
failure_class: FailureClass | None = None
repair_level: RepairLevel | None = None
repair_action: str | None = None
def as_dict(self) -> dict[str, object]:
return {
"schema": "aios.runtime-admission.decision.v1",
"admitted": self.admitted,
"state": self.state,
"invariants": [item.as_dict() for item in self.invariants],
"failure_class": None if self.failure_class is None else self.failure_class.value,
"repair_level": None if self.repair_level is None else int(self.repair_level),
"repair_action": self.repair_action,
}
@dataclass(frozen=True)
class RepairDecision:
failure_class: FailureClass
level: RepairLevel
action: str
consumes_business_attempt: bool
def as_dict(self) -> dict[str, object]:
return {
"failure_class": self.failure_class.value,
"level": int(self.level),
"action": self.action,
"consumes_business_attempt": self.consumes_business_attempt,
}
def classify_failure(error: object, *, phase: str = "") -> FailureClass:
text = (str(error) + " " + str(phase)).lower()
if any(token in text for token in (
"credential", "permission denied", "token registry", "secret", "unauthorized"
)):
return FailureClass.CREDENTIAL_PROJECTION_ERROR
if any(token in text for token in (
"transport", "unavailable", "timeout", "connection", "bad gateway", "http_5"
)):
return FailureClass.DEPENDENCY_UNAVAILABLE
if any(token in text for token in (
"capability", "eligible", "matcher", "contract"
)):
return FailureClass.CAPABILITY_CONTRACT_ERROR
if any(token in text for token in (
"policy", "forbidden", "approval required", "not authorized"
)):
return FailureClass.POLICY_DENIED
if any(token in text for token in ("lease lost", "lease_expired", "heartbeat_failed")):
return FailureClass.LEASE_LOST
if any(token in text for token in ("out of memory", "disk full", "resource")):
return FailureClass.RESOURCE_UNAVAILABLE
if any(token in text for token in ("worker crash", "process exited", "signal")):
return FailureClass.WORKER_CRASH
if any(token in text for token in ("invalid release", "business permanent")):
return FailureClass.BUSINESS_PERMANENT
return FailureClass.UNKNOWN
def repair_for_failure(failure: FailureClass) -> RepairDecision:
table = {
FailureClass.CAPABILITY_CONTRACT_ERROR: RepairDecision(
failure, RepairLevel.AUTO_REPAIR_TASK,
"create_same_lineage_capability_contract_repair", False,
),
FailureClass.CREDENTIAL_PROJECTION_ERROR: RepairDecision(
failure, RepairLevel.AUTO_REPAIR_TASK,
"restore_service_private_credential_projection", False,
),
FailureClass.DEPENDENCY_UNAVAILABLE: RepairDecision(
failure, RepairLevel.AUTO_LOCAL,
"retry_dependency_health_and_resume", False,
),
FailureClass.RESOURCE_UNAVAILABLE: RepairDecision(
failure, RepairLevel.AUTO_LOCAL,
"heal_runtime_resource_and_resume", False,
),
FailureClass.WORKER_CRASH: RepairDecision(
failure, RepairLevel.AUTO_LOCAL,
"restart_worker_and_resume_same_lineage", False,
),
FailureClass.LEASE_LOST: RepairDecision(
failure, RepairLevel.AUTO_LOCAL,
"recover_lease_and_resume_same_lineage", False,
),
FailureClass.POLICY_DENIED: RepairDecision(
failure, RepairLevel.OWNER_APPROVAL,
"request_bound_owner_authorization", False,
),
FailureClass.BUSINESS_PERMANENT: RepairDecision(
failure, RepairLevel.MANUAL,
"terminal_business_failure", True,
),
FailureClass.UNKNOWN: RepairDecision(
failure, RepairLevel.AUTO_REPAIR_TASK,
"create_diagnostic_repair_task", False,
),
}
return table[failure]
def _probe(
name: str,
probes: Mapping[str, Callable[[], bool]],
) -> InvariantResult:
fn = probes.get(name)
if fn is None:
return InvariantResult(name, False, "PROBE_MISSING")
try:
passed = bool(fn())
except Exception as exc:
return InvariantResult(name, False, "PROBE_ERROR:" + type(exc).__name__)
return InvariantResult(name, passed, "PASS" if passed else "NOT_READY")
def runtime_admission(
*,
worker_capabilities: Iterable[object],
required_capabilities: Iterable[object],
requested_operations: Sequence[str] = (),
probes: Mapping[str, Callable[[], bool]] | None = None,
) -> AdmissionDecision:
"""Admission is evaluated before claim; failures do not consume attempts."""
worker_values = tuple(worker_capabilities)
required_values = tuple(required_capabilities)
probe_map = probes or {}
invariants: list[InvariantResult] = []
registry = validate_capability_contract_registry()
invariants.append(InvariantResult(
"capability_contract_registry",
bool(registry.get("valid")),
"PASS" if registry.get("valid") else "REGISTRY_INVALID",
))
task_known = all(registered_capability(item) for item in required_values)
worker_known = all(registered_capability(item, worker=True) for item in worker_values)
invariants.extend((
InvariantResult("task_capabilities_registered", task_known, "PASS" if task_known else "UNKNOWN_TASK_CAPABILITY"),
InvariantResult("worker_capabilities_registered", worker_known, "PASS" if worker_known else "UNKNOWN_WORKER_CAPABILITY"),
))
capability_match = (
task_known
and worker_known
and has_capabilities(worker_values, required_values)
)
invariants.append(InvariantResult(
"capability_match",
capability_match,
"PASS" if capability_match else "WORKER_NOT_ELIGIBLE",
))
normalized = normalize_required_capabilities(required_values)
contracts = [
capability_contract(item)
for item in normalized
if not str(item).startswith("exact:")
]
contracts = [item for item in contracts if item is not None]
forbidden: set[str] = set()
dependencies: set[str] = set()
health: set[str] = set()
for contract in contracts:
forbidden.update(contract.forbidden)
dependencies.update(contract.dependencies)
health.update(contract.health_requirements)
requested = {str(item).strip().lower() for item in requested_operations if str(item).strip()}
forbidden_hit = sorted(requested & {item.lower() for item in forbidden})
invariants.append(InvariantResult(
"forbidden_operations_absent",
not forbidden_hit,
"PASS" if not forbidden_hit else "FORBIDDEN:" + ",".join(forbidden_hit),
))
for name in sorted(dependencies | health):
invariants.append(_probe(name, probe_map))
failed = [item for item in invariants if not item.passed]
if not failed:
return AdmissionDecision(
True,
"EXECUTION_READY",
tuple(invariants),
)
details = " ".join(item.name + ":" + item.detail for item in failed)
failure = classify_failure(details, phase="admission")
if any(item.name == "forbidden_operations_absent" and not item.passed for item in failed):
failure = FailureClass.POLICY_DENIED
elif any("credential" in item.name for item in failed):
failure = FailureClass.CREDENTIAL_PROJECTION_ERROR
elif any(item.name in {"capability_contract_registry", "task_capabilities_registered", "worker_capabilities_registered", "capability_match"} for item in failed):
failure = FailureClass.CAPABILITY_CONTRACT_ERROR
elif any(item.name in dependencies or item.name in health for item in failed):
failure = FailureClass.DEPENDENCY_UNAVAILABLE
repair = repair_for_failure(failure)
return AdmissionDecision(
False,
"EXECUTION_READY_FAILED",
tuple(invariants),
failure,
repair.level,
repair.action,
)
def system_invariant_snapshot(
*,
worker_capabilities: Iterable[object],
required_capabilities: Iterable[object],
requested_operations: Sequence[str] = (),
probes: Mapping[str, Callable[[], bool]] | None = None,
) -> dict[str, object]:
decision = runtime_admission(
worker_capabilities=worker_capabilities,
required_capabilities=required_capabilities,
requested_operations=requested_operations,
probes=probes,
)
return {
"schema": "aios.system-invariants.v1",
"status": "HEALTHY" if decision.admitted else "DEGRADED",
"execution_ready": decision.admitted,
"admission": decision.as_dict(),
}
@@ -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)
@@ -0,0 +1,341 @@
"""Canonical fail-closed capability semantics for task eligibility."""
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import asdict, dataclass
from typing import Any
_TASK_ALIASES: dict[str, frozenset[str]] = {
"repository_read": frozenset({"repo.read"}),
"git.read": frozenset({"repo.read"}),
"repo.read": frozenset({"repo.read"}),
"test_execution": frozenset({"test.execute"}),
"git.test": frozenset({"test.execute"}),
"test.execute": frozenset({"test.execute"}),
"governed_git_write": frozenset({"governed.git.write.branch", "governed.git.write.file"}),
"governed.git.write": frozenset({"governed.git.write.branch", "governed.git.write.file"}),
"governed.write.branch": frozenset({"governed.git.write.branch"}),
"governed.write.file": frozenset({"governed.git.write.file"}),
"shell": frozenset({"runtime.shell"}),
"git": frozenset({"runtime.git"}),
"python": frozenset({"runtime.python"}),
"code.read": frozenset({"legacy.code.read"}),
"code.review": frozenset({"legacy.code.review"}),
"code.write": frozenset({"legacy.code.write"}),
"code.write.patch": frozenset({"legacy.code.write.patch"}),
"test.python": frozenset({"legacy.test.python"}),
"test.shell.allowlisted": frozenset({"legacy.test.shell.allowlisted"}),
"git.commit": frozenset({"legacy.git.commit"}),
"git.diff.read": frozenset({"legacy.git.diff.read"}),
"git.force_push": frozenset({"legacy.git.force_push"}),
"deploy.production": frozenset({"legacy.deploy.production"}),
"secret.transform": frozenset({"legacy.secret.transform"}),
"secure_file_transformer": frozenset({"legacy.secure_file_transformer"}),
"runtime.shadow": frozenset({"legacy.runtime.shadow"}),
"review.security": frozenset({"legacy.review.security"}),
"review.api-contract": frozenset({"legacy.review.api-contract"}),
"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"}),
}
_WORKER_ALIASES: dict[str, frozenset[str]] = {
"git.read": frozenset({"repo.read"}),
"repo.read": frozenset({"repo.read"}),
"repository_read": frozenset({"repo.read"}),
"git.test": frozenset({"test.execute"}),
"test.execute": frozenset({"test.execute"}),
"test_execution": frozenset({"test.execute"}),
"governed.write.branch": frozenset({"governed.git.write.branch"}),
"governed.git.write.branch": frozenset({"governed.git.write.branch"}),
"governed.write.file": frozenset({"governed.git.write.file"}),
"governed.git.write.file": frozenset({"governed.git.write.file"}),
"shell": frozenset({"runtime.shell"}),
"git": frozenset({"runtime.git"}),
"python": frozenset({"runtime.python"}),
"code.read": frozenset({"legacy.code.read"}),
"code.review": frozenset({"legacy.code.review"}),
"code.write": frozenset({"legacy.code.write"}),
"code.write.patch": frozenset({"legacy.code.write.patch"}),
"test.python": frozenset({"legacy.test.python"}),
"test.shell.allowlisted": frozenset({"legacy.test.shell.allowlisted"}),
"git.commit": frozenset({"legacy.git.commit"}),
"git.diff.read": frozenset({"legacy.git.diff.read"}),
"git.force_push": frozenset({"legacy.git.force_push"}),
"deploy.production": frozenset({"legacy.deploy.production"}),
"secret.transform": frozenset({"legacy.secret.transform"}),
"secure_file_transformer": frozenset({"legacy.secure_file_transformer"}),
"runtime.shadow": frozenset({"legacy.runtime.shadow"}),
"review.security": frozenset({"legacy.review.security"}),
"review.api-contract": frozenset({"legacy.review.api-contract"}),
"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"}),
}
@dataclass(frozen=True)
class CapabilityContract:
"""Versioned runtime contract attached to one canonical semantic capability."""
name: str
min_version: int = 1
max_version: int = 1
permissions: tuple[str, ...] = ()
forbidden: tuple[str, ...] = ()
dependencies: tuple[str, ...] = ()
health_requirements: tuple[str, ...] = ()
def as_dict(self) -> dict[str, Any]:
return asdict(self)
# This registry is the semantic source of truth. Alias tables below are retained
# as compatibility adapters; startup/tests validate every alias target against
# this registry so a name cannot be accepted in one layer and be unknown in
# another.
_CAPABILITY_CONTRACTS: dict[str, CapabilityContract] = {
"repo.read": CapabilityContract("repo.read", permissions=("repository_read",)),
"test.execute": CapabilityContract("test.execute", permissions=("test_execution",)),
"governed.git.write.branch": CapabilityContract(
"governed.git.write.branch",
permissions=("governed_branch_write",),
dependencies=("writer_lease", "governed_gitops"),
),
"governed.git.write.file": CapabilityContract(
"governed.git.write.file",
permissions=("governed_file_write",),
dependencies=("writer_lease", "governed_gitops"),
),
"governed.git.write": CapabilityContract(
"governed.git.write",
permissions=("governed_git_write",),
dependencies=("writer_lease", "governed_gitops"),
),
"runtime.shell": CapabilityContract("runtime.shell", permissions=("runtime_shell",)),
"runtime.git": CapabilityContract("runtime.git", permissions=("runtime_git",)),
"runtime.python": CapabilityContract("runtime.python", permissions=("runtime_python",)),
"legacy.code.read": CapabilityContract("legacy.code.read"),
"legacy.code.review": CapabilityContract("legacy.code.review"),
"legacy.code.write": CapabilityContract("legacy.code.write"),
"legacy.code.write.patch": CapabilityContract("legacy.code.write.patch"),
"legacy.test.python": CapabilityContract("legacy.test.python"),
"legacy.test.shell.allowlisted": CapabilityContract("legacy.test.shell.allowlisted"),
"legacy.git.commit": CapabilityContract("legacy.git.commit"),
"legacy.git.diff.read": CapabilityContract("legacy.git.diff.read"),
"legacy.git.force_push": CapabilityContract(
"legacy.git.force_push",
forbidden=("protected_ref_bypass",),
),
"legacy.deploy.production": CapabilityContract(
"legacy.deploy.production",
permissions=("production_deploy",),
dependencies=("owner_approval", "deployment_controller"),
health_requirements=("approval_envelope_verified", "runtime_acceptance_available"),
),
"legacy.secret.transform": CapabilityContract("legacy.secret.transform"),
"legacy.secure_file_transformer": CapabilityContract("legacy.secure_file_transformer"),
"legacy.runtime.shadow": CapabilityContract("legacy.runtime.shadow"),
"legacy.review.security": CapabilityContract("legacy.review.security"),
"legacy.review.api-contract": CapabilityContract("legacy.review.api-contract"),
"legacy.gpu.train": CapabilityContract("legacy.gpu.train"),
"agent.runtime.hermes": CapabilityContract(
"agent.runtime.hermes",
dependencies=("hermes_runtime",),
health_requirements=("worker_liveness",),
),
"agent.runtime.openclaw": CapabilityContract(
"agent.runtime.openclaw",
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,
max_version=1,
permissions=("prepare_release",),
forbidden=(
"production_deploy",
"submit_owner_approval",
"tag_publish",
"baseline_registry_finalize",
"runtime_switch",
),
dependencies=(
"credential_projection",
"trusted_builder",
),
health_requirements=(
"authenticated_probe",
"release_api_health",
"worker_liveness",
),
),
}
def capability_contract(name: object) -> CapabilityContract | None:
"""Return the contract for one canonical or registered capability name."""
text = _clean(name)
direct = _CAPABILITY_CONTRACTS.get(text)
if direct is not None:
return direct
mapped = _TASK_ALIASES.get(text) or _WORKER_ALIASES.get(text)
if mapped is None or len(mapped) != 1:
return None
return _CAPABILITY_CONTRACTS.get(next(iter(mapped)))
def registered_capability(name: object, *, worker: bool = False) -> bool:
"""Fail-closed validation used by registration/task admission."""
text = _clean(name)
if "*" in text:
return False
aliases = _WORKER_ALIASES if worker else _TASK_ALIASES
mapped = aliases.get(text)
if mapped is None:
return text in _CAPABILITY_CONTRACTS
return all(item in _CAPABILITY_CONTRACTS for item in mapped)
def validate_capability_contract_registry() -> dict[str, object]:
"""Prove alias/contract closure and contract safety invariants."""
alias_targets: set[str] = set()
for mapping in (_TASK_ALIASES, _WORKER_ALIASES):
for values in mapping.values():
alias_targets.update(values)
missing = sorted(alias_targets - set(_CAPABILITY_CONTRACTS))
invalid_versions = sorted(
name
for name, contract in _CAPABILITY_CONTRACTS.items()
if contract.min_version < 1 or contract.max_version < contract.min_version
)
permission_forbidden_overlap = sorted(
name
for name, contract in _CAPABILITY_CONTRACTS.items()
if set(contract.permissions) & set(contract.forbidden)
)
return {
"schema": "aios.capability-contract-registry.v1",
"contract_count": len(_CAPABILITY_CONTRACTS),
"missing_alias_targets": missing,
"invalid_versions": invalid_versions,
"permission_forbidden_overlap": permission_forbidden_overlap,
"valid": not missing and not invalid_versions and not permission_forbidden_overlap,
}
def capability_contract_snapshot() -> dict[str, dict[str, Any]]:
return {
name: contract.as_dict()
for name, contract in sorted(_CAPABILITY_CONTRACTS.items())
}
_UNKNOWN_PREFIX = "exact:"
def _clean(value: object) -> str:
if not isinstance(value, str):
raise ValueError("capability must be a string")
text = value.strip().lower()
if not text:
raise ValueError("capability must not be empty")
return text
def normalize_required_capabilities(values: Iterable[object]) -> frozenset[str]:
"""Map task-facing names to one semantic vocabulary.
Unknown names remain explicit exact requirements. They are never treated
as wildcards or granted by a failed normalization.
"""
result: set[str] = set()
for value in values:
text = _clean(value)
if "*" in text:
result.add(_UNKNOWN_PREFIX + text)
else:
mapped = _TASK_ALIASES.get(text)
if mapped is None or any(item not in _CAPABILITY_CONTRACTS for item in mapped):
result.add(_UNKNOWN_PREFIX + text)
else:
result.update(mapped)
return frozenset(result)
def normalize_worker_capabilities(values: Iterable[object]) -> frozenset[str]:
"""Map registered worker names to effective semantic capabilities."""
result: set[str] = set()
for value in values:
text = _clean(value)
if "*" in text:
continue
mapped = _WORKER_ALIASES.get(text)
if mapped is not None and all(item in _CAPABILITY_CONTRACTS for item in mapped):
result.update(mapped)
if {
"governed.git.write.branch",
"governed.git.write.file",
}.issubset(result):
result.add("governed.git.write")
return frozenset(result)
def has_capabilities(
worker_capabilities: Iterable[object],
required_capabilities: Iterable[object],
) -> bool:
"""Return the single eligibility decision used by poll and claim."""
required = normalize_required_capabilities(required_capabilities)
effective = normalize_worker_capabilities(worker_capabilities)
if any(value.startswith(_UNKNOWN_PREFIX) for value in required):
return False
return required.issubset(effective)
def required_capabilities_subset(
granted_requirements: Iterable[object],
required_requirements: Iterable[object],
) -> bool:
"""Check semantic capability containment for delegation constraints."""
granted = normalize_required_capabilities(granted_requirements)
required = normalize_required_capabilities(required_requirements)
if any(value.startswith(_UNKNOWN_PREFIX) for value in granted | required):
return False
return required.issubset(granted)
def normalized_capability_matrix(
worker_capabilities: Iterable[object],
required_capabilities: Iterable[object],
) -> dict[str, object]:
"""Expose non-secret decision evidence for tests and diagnostics."""
worker_values = tuple(worker_capabilities)
required_values = tuple(required_capabilities)
worker = normalize_worker_capabilities(worker_values)
required = normalize_required_capabilities(required_values)
return {
"worker_effective": sorted(worker),
"task_required": sorted(required),
"eligible": has_capabilities(worker_values, required_values),
}
@@ -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()
File diff suppressed because it is too large Load Diff
+410 -67
View File
@@ -16,6 +16,11 @@ import subprocess
import tempfile
import time
from typing import Any, Mapping, Sequence
from aios_model_governance import (
ModelGovernanceError,
ModelGovernanceStore,
RouteRequest,
)
REQUEST_SCHEMA = "aios.external-agent-runtime.request.v1"
RESULT_SCHEMA = "aios.external-agent-runtime.result.v1"
@@ -62,6 +67,15 @@ MAX_PROFILE_BYTES = 64 * 1024
MAX_CREDENTIAL_BYTES = 64 * 1024
DEFAULT_PROFILE_ROOT = Path("/etc/aios/external-agent-runtime.d")
DEFAULT_EXCHANGE_ROOT = Path("/var/lib/aios-external-agent-executor")
DEFAULT_MODEL_GOVERNANCE_DB = Path(
os.environ.get(
"AIOS_MODEL_GOVERNANCE_DB",
"/var/lib/aios-model-governance/state.sqlite3",
)
)
MODEL_GOVERNANCE_ENABLED = os.environ.get(
"AIOS_MODEL_GOVERNANCE_ENABLED", "0"
).strip().lower() in {"1", "true", "yes", "on"}
ALLOWED_EXECUTABLE_ROOTS = (
Path("/usr/bin"),
Path("/usr/local/bin"),
@@ -208,6 +222,172 @@ def _profile_credential_environment(
return tuple(sorted(result))
def _profile_model_governance(
value: object,
) -> tuple[bool, tuple[str, ...], tuple[str, ...], int, int]:
if value is None:
return False, (), (), 0, 0
if not isinstance(value, Mapping):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_GOVERNANCE_PROFILE_INVALID")
allowed = {
"required",
"provider_ids",
"model_capabilities",
"max_output_tokens",
"input_reservation_floor_tokens",
}
if set(value) - allowed:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_GOVERNANCE_PROFILE_FIELDS_INVALID")
required = bool(value.get("required", False))
providers = value.get("provider_ids") or []
capabilities = value.get("model_capabilities") or []
if (
not isinstance(providers, Sequence)
or isinstance(providers, (str, bytes))
or not all(isinstance(item, str) and item.strip() for item in providers)
):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_PROVIDER_IDS_INVALID")
if (
not isinstance(capabilities, Sequence)
or isinstance(capabilities, (str, bytes))
or not all(isinstance(item, str) and item.strip() for item in capabilities)
):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_CAPABILITIES_INVALID")
max_output_tokens = int(value.get("max_output_tokens") or 0)
input_reservation_floor_tokens = int(
value.get("input_reservation_floor_tokens") or 0
)
if max_output_tokens < 0 or max_output_tokens > 262144:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_OUTPUT_LIMIT_INVALID")
if (
input_reservation_floor_tokens < 0
or input_reservation_floor_tokens > 262144
):
raise ExternalAgentRuntimeError(
"EXTERNAL_AGENT_MODEL_INPUT_RESERVATION_FLOOR_INVALID"
)
if required and not providers:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_PROVIDER_IDS_REQUIRED")
if required and max_output_tokens < 1:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_OUTPUT_LIMIT_REQUIRED")
return (
required,
tuple(sorted({item.strip() for item in providers})),
tuple(sorted({item.strip() for item in capabilities})),
max_output_tokens,
input_reservation_floor_tokens,
)
def _estimate_tokens(text: str) -> int:
# Conservative byte-based estimate; runtime-reported usage supersedes this at settlement.
data = text.encode("utf-8", "replace")
return max(1, (len(data) + 3) // 4)
def _extract_runtime_usage(
output_json: object,
) -> tuple[int | None, int | None, int | None]:
if not isinstance(output_json, Mapping):
return None, None, None
candidates: list[Mapping[str, Any]] = []
direct = output_json.get("usage")
if isinstance(direct, Mapping):
candidates.append(direct)
meta = output_json.get("meta")
if isinstance(meta, Mapping):
agent_meta = meta.get("agentMeta")
if isinstance(agent_meta, Mapping):
nested = agent_meta.get("usage")
if isinstance(nested, Mapping):
candidates.append(nested)
last_call = agent_meta.get("lastCallUsage")
if isinstance(last_call, Mapping):
candidates.append(last_call)
def number(mapping: Mapping[str, Any], *names: str) -> int | None:
for name in names:
value = mapping.get(name)
if isinstance(value, int) and not isinstance(value, bool) and value >= 0:
return value
return None
input_tokens = None
output_tokens = None
cost_micros = None
for usage in candidates:
if input_tokens is None:
input_tokens = number(usage, "input_tokens", "prompt_tokens", "input")
if output_tokens is None:
output_tokens = number(
usage, "output_tokens", "completion_tokens", "output"
)
if cost_micros is None:
cost_micros = number(usage, "cost_micros", "total_cost_micros")
if cost_micros is None and isinstance(meta, Mapping):
agent_meta = meta.get("agentMeta")
if isinstance(agent_meta, Mapping):
cost_usd = agent_meta.get("costUsd")
if (
isinstance(cost_usd, (int, float))
and not isinstance(cost_usd, bool)
and cost_usd >= 0
):
cost_micros = int(round(float(cost_usd) * 1_000_000))
return input_tokens, output_tokens, cost_micros
def _scope_from_payload(payload: Mapping[str, Any]) -> tuple[str, str | None, str | None, str]:
scope = payload.get("scope")
if not isinstance(scope, Mapping):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_SCOPE_MISSING")
scope_type = str(scope.get("scope_type") or "legacy")
tenant_id = scope.get("tenant_id")
project_id = scope.get("project_id")
trace_id = str(payload.get("trace_id") or "").strip()
if not trace_id:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_TRACE_ID_MISSING")
return (
scope_type,
str(tenant_id) if tenant_id not in (None, "") else None,
str(project_id) if project_id not in (None, "") else None,
trace_id,
)
def _model_replacements(decision: object | None) -> dict[str, str]:
if decision is None:
return {}
return {
"{provider_id}": str(getattr(decision, "provider_id")),
"{model_id}": str(getattr(decision, "model_id")),
"{model_version}": str(getattr(decision, "model_version")),
}
def _bind_model_values(
values: Sequence[tuple[str, str]],
decision: object | None,
) -> tuple[tuple[str, str], ...]:
replacements = _model_replacements(decision)
result: list[tuple[str, str]] = []
for key, value in values:
bound = value
for marker, replacement in replacements.items():
bound = bound.replace(marker, replacement)
result.append((key, bound))
return tuple(result)
def _runtime_model_binding_present(profile: "RuntimeProfile") -> bool:
markers = ("{model_id}", "{model_version}", "{provider_id}")
return any(marker in item for item in (*profile.argv, *(value for _, value in profile.environment)) for marker in markers)
@dataclass(frozen=True)
class RuntimeProfile:
runtime: str
@@ -218,6 +398,11 @@ class RuntimeProfile:
max_output_bytes: int
environment: tuple[tuple[str, str], ...]
credential_env: tuple[tuple[str, str, str, str], ...]
model_governance_required: bool
model_provider_ids: tuple[str, ...]
model_capabilities: tuple[str, ...]
model_max_output_tokens: int
model_input_reservation_floor_tokens: int
@classmethod
def load(
@@ -275,7 +460,14 @@ class RuntimeProfile:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_OUTPUT_LIMIT_INVALID")
environment = _profile_environment(value.get("environment"))
credential_env = _profile_credential_environment(value.get("credential_env"))
return cls(
(
model_governance_required,
model_provider_ids,
model_capabilities,
model_max_output_tokens,
model_input_reservation_floor_tokens,
) = _profile_model_governance(value.get("model_governance"))
profile = cls(
runtime,
tuple(normalized),
input_mode,
@@ -284,7 +476,15 @@ class RuntimeProfile:
max_output,
environment,
credential_env,
model_governance_required,
model_provider_ids,
model_capabilities,
model_max_output_tokens,
model_input_reservation_floor_tokens,
)
if profile.model_governance_required and not _runtime_model_binding_present(profile):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_BINDING_REQUIRED")
return profile
def validate_request(value: Mapping[str, Any]) -> dict[str, Any]:
@@ -295,9 +495,16 @@ def validate_request(value: Mapping[str, Any]) -> dict[str, Any]:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_REQUEST_ID_INVALID")
runtime = _safe_runtime(value.get("runtime"))
task_id = str(value.get("task_id") or "")
attempt = value.get("attempt")
execution_digest = str(value.get("execution_digest") or "")
if not task_id or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:-]{2,179}", task_id):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_TASK_ID_INVALID")
if (
not isinstance(attempt, int)
or isinstance(attempt, bool)
or not 1 <= attempt <= 1000
):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_ATTEMPT_INVALID")
if re.fullmatch(r"[0-9a-f]{64}", execution_digest) is None:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_EXECUTION_DIGEST_INVALID")
payload = value.get("payload")
@@ -317,6 +524,7 @@ def validate_request(value: Mapping[str, Any]) -> dict[str, Any]:
"request_id": request_id,
"runtime": runtime,
"task_id": task_id,
"attempt": attempt,
"execution_digest": execution_digest,
"payload": dict(payload),
}
@@ -351,11 +559,13 @@ def _argv_with_files(
input_path: Path,
output_path: Path,
prompt: str,
model_decision: object | None = None,
) -> list[str]:
replacements = {
"{input_json}": str(input_path),
"{output_json}": str(output_path),
"{prompt}": prompt,
**_model_replacements(model_decision),
}
result = []
for item in profile.argv:
@@ -471,7 +681,12 @@ def execute_request(
request = validate_request(json.loads(raw_request.decode("utf-8")))
except json.JSONDecodeError as exc:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_REQUEST_JSON_INVALID") from exc
profile = RuntimeProfile.load(request["runtime"], profile_root)
governance_store: ModelGovernanceStore | None = None
route_request: RouteRequest | None = None
decision = None
governance_attempts: list[dict[str, Any]] = []
with tempfile.TemporaryDirectory(prefix=f"aios-{request['runtime']}-") as temporary:
temp_root = Path(temporary)
@@ -480,78 +695,203 @@ def execute_request(
output_path = temp_root / "output.json"
input_path.write_bytes(canonical(request["payload"]))
prompt = render_prompt(request["payload"])
argv = _argv_with_files(profile, input_path, output_path, prompt)
stdin_data = None
if profile.input_mode == "stdin_json":
stdin_data = canonical(request["payload"]).decode("utf-8")
elif profile.input_mode == "stdin_text":
stdin_data = prompt
child_env = {
"PATH": "/usr/bin:/usr/local/bin:/bin",
"LC_ALL": "C.UTF-8",
"LANG": "C.UTF-8",
"HOME": str(temp_root / "home"),
"NO_COLOR": "1",
}
child_env.update(dict(profile.environment))
child_env.update(_credential_environment(profile))
started = time.monotonic()
try:
completed = subprocess.run(
argv,
input=stdin_data,
capture_output=True,
text=True,
check=False,
shell=False,
timeout=profile.timeout_seconds,
cwd=str(temp_root),
env=child_env,
if profile.model_governance_required:
if not MODEL_GOVERNANCE_ENABLED:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_GOVERNANCE_DISABLED")
scope_type, tenant_id, project_id, trace_id = _scope_from_payload(request["payload"])
governance_store = ModelGovernanceStore(DEFAULT_MODEL_GOVERNANCE_DB)
prompt_estimated_input_tokens = _estimate_tokens(prompt)
reserved_estimated_input_tokens = max(
prompt_estimated_input_tokens,
profile.model_input_reservation_floor_tokens,
)
except subprocess.TimeoutExpired as exc:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_TIMEOUT") from exc
if completed.returncode != 0:
raise ExternalAgentRuntimeError(
f"EXTERNAL_AGENT_PROCESS_FAILED:{completed.returncode}:"
+ _bounded_text(completed.stderr, 2048)
route_request = RouteRequest(
request_id=request["request_id"] + ":model",
task_id=request["task_id"],
trace_id=trace_id,
scope_type=scope_type,
tenant_id=tenant_id,
project_id=project_id,
required_capabilities=tuple(
sorted(
{
f"agent.runtime.{request['runtime']}",
*profile.model_capabilities,
}
)
),
estimated_input_tokens=reserved_estimated_input_tokens,
max_output_tokens=profile.model_max_output_tokens,
)
try:
decision = governance_store.route(route_request)
except ModelGovernanceError as exc:
raise ExternalAgentRuntimeError(exc.code) from exc
if profile.model_provider_ids and decision.provider_id not in profile.model_provider_ids:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_PROVIDER_PROFILE_DENY")
governance_attempts.append(
{
"request_id": decision.request_id,
"provider_id": decision.provider_id,
"model_id": decision.model_id,
"model_version": decision.model_version,
"fallback_index": decision.fallback_index,
}
)
output_text, output_json = _parse_output(profile, completed.stdout, output_path)
result: dict[str, Any] = {
"schema": RESULT_SCHEMA,
"request_id": request["request_id"],
"request_sha256": sha256(canonical(request)),
"runtime": request["runtime"],
"task_id": request["task_id"],
"execution_digest": request["execution_digest"],
"exit_code": completed.returncode,
"duration_ms": int((time.monotonic() - started) * 1000),
"output_text": output_text,
"output_sha256": sha256(output_text.encode("utf-8", "replace")),
"stderr_sha256": sha256(completed.stderr.encode("utf-8", "replace")),
"credential_material_exposed": False,
"control_plane_access_granted": False,
}
if output_json is not None:
result["output_json"] = output_json
result["result_sha256"] = sha256(canonical(result))
result_path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{result_path.name}.", dir=result_path.parent)
try:
os.fchmod(fd, 0o640)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(result, handle, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(name, result_path)
while True:
argv = _argv_with_files(
profile,
input_path,
output_path,
prompt,
decision,
)
stdin_data = None
if profile.input_mode == "stdin_json":
stdin_data = canonical(request["payload"]).decode("utf-8")
elif profile.input_mode == "stdin_text":
stdin_data = prompt
child_env = {
"PATH": "/usr/bin:/usr/local/bin:/bin",
"LC_ALL": "C.UTF-8",
"LANG": "C.UTF-8",
"HOME": str(temp_root / "home"),
"NO_COLOR": "1",
}
child_env.update(dict(_bind_model_values(profile.environment, decision)))
child_env.update(_credential_environment(profile))
started = time.monotonic()
failure_class = None
try:
completed = subprocess.run(
argv,
input=stdin_data,
capture_output=True,
text=True,
check=False,
shell=False,
timeout=profile.timeout_seconds,
cwd=str(temp_root),
env=child_env,
)
if completed.returncode != 0:
failure_class = f"process_exit_{completed.returncode}"
except subprocess.TimeoutExpired:
completed = None
failure_class = "timeout"
if failure_class is None and completed is not None:
break
if governance_store is None or decision is None or route_request is None:
if failure_class == "timeout":
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_TIMEOUT")
raise ExternalAgentRuntimeError(
f"EXTERNAL_AGENT_PROCESS_FAILED:{completed.returncode}:"
+ _bounded_text(completed.stderr, 2048)
)
try:
governance_store.record_failure(decision.request_id, failure_class)
decision = governance_store.fallback(route_request, decision.request_id)
except ModelGovernanceError as exc:
raise ExternalAgentRuntimeError(exc.code) from exc
if profile.model_provider_ids and decision.provider_id not in profile.model_provider_ids:
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_MODEL_PROVIDER_PROFILE_DENY")
governance_attempts.append(
{
"request_id": decision.request_id,
"provider_id": decision.provider_id,
"model_id": decision.model_id,
"model_version": decision.model_version,
"fallback_index": decision.fallback_index,
"previous_failure_class": failure_class,
}
)
output_text, output_json = _parse_output(profile, completed.stdout, output_path)
model_governance: dict[str, Any] | None = None
if governance_store is not None and decision is not None:
input_tokens, output_tokens, actual_cost = _extract_runtime_usage(output_json)
try:
settlement = governance_store.record_success(
decision.request_id,
input_tokens=input_tokens,
output_tokens=output_tokens,
actual_cost_micros=actual_cost,
usage_source="runtime",
)
except ModelGovernanceError as exc:
raise ExternalAgentRuntimeError(exc.code) from exc
model_governance = {
"decision_id": decision.decision_id,
"request_id": decision.request_id,
"provider_id": decision.provider_id,
"model_id": decision.model_id,
"model_version": decision.model_version,
"policy_revision": decision.policy_revision,
"model_revision": decision.model_revision,
"fallback_index": decision.fallback_index,
"prompt_estimated_input_tokens": prompt_estimated_input_tokens,
"input_reservation_floor_tokens": (
profile.model_input_reservation_floor_tokens
),
"reserved_estimated_input_tokens": (
reserved_estimated_input_tokens
),
"settlement": settlement,
"attempts": governance_attempts,
"audit_chain_valid": governance_store.verify_audit_chain(),
}
result: dict[str, Any] = {
"schema": RESULT_SCHEMA,
"request_id": request["request_id"],
"request_sha256": sha256(canonical(request)),
"runtime": request["runtime"],
"task_id": request["task_id"],
"attempt": request["attempt"],
"execution_digest": request["execution_digest"],
"exit_code": completed.returncode,
"duration_ms": int((time.monotonic() - started) * 1000),
"output_text": output_text,
"output_sha256": sha256(output_text.encode("utf-8", "replace")),
"stderr_sha256": sha256(completed.stderr.encode("utf-8", "replace")),
"credential_material_exposed": False,
"control_plane_access_granted": False,
}
if output_json is not None:
result["output_json"] = output_json
if model_governance is not None:
result["model_governance"] = model_governance
result["result_sha256"] = sha256(canonical(result))
result_path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{result_path.name}.", dir=result_path.parent)
try:
os.fchmod(fd, 0o640)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(
result,
handle,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(name, result_path)
finally:
if os.path.exists(name):
os.unlink(name)
finally:
if os.path.exists(name):
os.unlink(name)
if governance_store is not None:
governance_store.close()
class ExternalAgentExchange:
@@ -566,6 +906,7 @@ class ExternalAgentExchange:
request_id: str,
runtime: str,
task_id: str,
attempt: int,
execution_digest: str,
payload: Mapping[str, Any],
timeout_seconds: int = 1800,
@@ -576,6 +917,7 @@ class ExternalAgentExchange:
"request_id": request_id,
"runtime": runtime,
"task_id": task_id,
"attempt": attempt,
"execution_digest": execution_digest,
"payload": dict(payload),
}
@@ -651,6 +993,7 @@ class ExternalAgentExchange:
or value.get("request_sha256") != request_sha
or value.get("runtime") != request["runtime"]
or value.get("task_id") != request["task_id"]
or value.get("attempt") != request["attempt"]
or value.get("execution_digest") != request["execution_digest"]
):
raise ExternalAgentRuntimeError("EXTERNAL_AGENT_RESULT_BINDING_INVALID")
@@ -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)
+456
View File
@@ -0,0 +1,456 @@
#!/usr/bin/env python3
"""Disabled-by-default TLS edge for the read-only GPT Online Task ingress.
The internal AIOS ingress remains loopback-only. This service is a separate edge
that may bind non-loopback only when the owner explicitly approves it. It
forwards only the tiny read-only ingress API and never proxies the canonical
Bridge or Task API.
"""
from __future__ import annotations
from collections import defaultdict, deque
from dataclasses import dataclass
import hashlib
import hmac
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import os
from pathlib import Path
import re
import secrets
import ssl
import sys
import threading
import time
import urllib.error
import urllib.request
from typing import Any, Mapping
from urllib.parse import urlsplit
UPSTREAM = "http://127.0.0.1:18945"
MAX_BODY = 64 * 1024
MAX_RESPONSE = 256 * 1024
MAX_CONCURRENT_REQUESTS = 32
REQUEST_TIMEOUT_SECONDS = 20
RATE_WINDOW_SECONDS = 60
RATE_LIMIT_REQUESTS = 60
RATE_BURST_REQUESTS = 15
RATE_MAX_CLIENTS = 4096
_AUDIT_SALT = secrets.token_bytes(32)
PUBLIC_APPROVAL_ENV = "AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED"
PUBLIC_DOMAIN_ENV = "AIOS_GPT_EDGE_PUBLIC_DOMAIN"
TASK_PATH_RE = re.compile(r"^/v1/read-only-tasks(?:/[A-Za-z0-9][A-Za-z0-9._:-]{7,127})?$")
def _client_pseudonym(value: str) -> str:
return hashlib.sha256(_AUDIT_SALT + value.encode("utf-8", "replace")).hexdigest()[:16]
def audit(event: str, **fields: object) -> None:
safe = {
"event": event,
"ts_unix_ms": int(time.time() * 1000),
}
for key, value in fields.items():
if key in {"authorization", "body", "token", "cookie", "client_ip"}:
continue
safe[key] = value
print(
json.dumps(safe, sort_keys=True, separators=(",", ":")),
file=sys.stderr,
flush=True,
)
class EdgeError(RuntimeError):
def __init__(self, code: str, status: int = 400) -> None:
super().__init__(code)
self.code = code
self.status = status
def canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def public_bind_allowed(host: str, approval: str) -> str:
value = host.strip()
if value in {"127.0.0.1", "::1", "localhost"}:
return value
if approval != "1":
raise RuntimeError("GPT_EDGE_PUBLIC_BIND_OWNER_APPROVAL_REQUIRED")
if value not in {"0.0.0.0", "::"}:
raise RuntimeError("GPT_EDGE_BIND_ADDRESS_INVALID")
return value
def validated_public_domain(value: str) -> str:
domain = value.strip().lower()
if not domain:
return ""
if len(domain) > 253 or re.fullmatch(
r"(?=.{1,253}\Z)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?",
domain,
) is None:
raise RuntimeError("GPT_EDGE_PUBLIC_DOMAIN_INVALID")
return domain
def validated_edge_identity(host: str, approval: str, domain: str) -> tuple[str, str]:
bind_host = public_bind_allowed(host, approval)
public_domain = validated_public_domain(domain)
if bind_host not in {"127.0.0.1", "::1", "localhost"} and not public_domain:
raise RuntimeError("GPT_EDGE_PUBLIC_DOMAIN_REQUIRED")
return bind_host, public_domain
def validate_tls_file(path: Path, *, key: bool) -> Path:
if path.is_symlink() or not path.is_file():
raise RuntimeError("GPT_EDGE_TLS_FILE_INVALID")
size = path.stat().st_size
if size < 16 or size > 256 * 1024:
raise RuntimeError("GPT_EDGE_TLS_FILE_INVALID")
if key and (path.stat().st_mode & 0o077):
raise RuntimeError("GPT_EDGE_TLS_KEY_MODE_INVALID")
return path
def build_tls_context(cert_path: Path, key_path: Path) -> ssl.SSLContext:
cert = validate_tls_file(cert_path, key=False)
key = validate_tls_file(key_path, key=True)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.options |= ssl.OP_NO_COMPRESSION
context.load_cert_chain(certfile=str(cert), keyfile=str(key))
return context
def allowed_route(method: str, raw_path: str) -> str:
parsed = urlsplit(raw_path)
if parsed.query or parsed.fragment:
raise EdgeError("QUERY_OR_FRAGMENT_DENY", 400)
path = parsed.path
if method == "GET" and path in {"/health", "/openapi.json"}:
return path
if method == "POST" and path == "/v1/read-only-tasks":
return path
if method == "GET" and TASK_PATH_RE.fullmatch(path):
return path
raise EdgeError("ROUTE_DENY", 404)
def route_label(method: str, path: str) -> str:
if method == "GET" and path == "/health":
return "health"
if method == "GET" and path == "/openapi.json":
return "openapi"
if method == "POST" and path == "/v1/read-only-tasks":
return "task_create"
if method == "GET" and path.startswith("/v1/read-only-tasks/"):
return "task_status"
return "unknown"
def forwarded_headers(headers: Mapping[str, str], *, body: bool) -> dict[str, str]:
result = {"Accept": "application/json"}
authorization = headers.get("Authorization")
if not isinstance(authorization, str) or not authorization.startswith("Bearer "):
raise EdgeError("AUTHORIZATION_REQUIRED", 401)
token = authorization[7:]
if len(token) < 32 or len(token) > 4096 or any(ch.isspace() for ch in token):
raise EdgeError("AUTHORIZATION_INVALID", 401)
result["Authorization"] = authorization
if body:
content_type = headers.get("Content-Type", "")
if content_type.split(";", 1)[0].strip().lower() != "application/json":
raise EdgeError("CONTENT_TYPE_INVALID", 415)
result["Content-Type"] = "application/json"
return result
class RateLimiter:
def __init__(
self,
*,
window_seconds: int = RATE_WINDOW_SECONDS,
limit: int = RATE_LIMIT_REQUESTS,
burst: int = RATE_BURST_REQUESTS,
max_clients: int = RATE_MAX_CLIENTS,
now_fn=time.monotonic,
) -> None:
self.window_seconds = window_seconds
self.limit = limit
self.burst = burst
self.max_clients = max_clients
self.now_fn = now_fn
self._lock = threading.Lock()
self._events: dict[str, deque[float]] = defaultdict(deque)
def _prune_stale(self, cutoff: float) -> None:
stale = [
key
for key, events in self._events.items()
if not events or events[-1] <= cutoff
]
for key in stale:
self._events.pop(key, None)
def allowed(self, key: str) -> bool:
now = self.now_fn()
cutoff = now - self.window_seconds
with self._lock:
if key not in self._events and len(self._events) >= self.max_clients:
self._prune_stale(cutoff)
if len(self._events) >= self.max_clients:
return False
events = self._events[key]
while events and events[0] <= cutoff:
events.popleft()
if len(events) >= self.limit + self.burst:
return False
events.append(now)
return True
@dataclass(frozen=True)
class Proxy:
upstream: str = UPSTREAM
timeout: int = REQUEST_TIMEOUT_SECONDS
def request(
self,
method: str,
path: str,
headers: Mapping[str, str],
body: bytes | None,
) -> tuple[int, bytes, str]:
if self.upstream != UPSTREAM:
raise EdgeError("UPSTREAM_DRIFT_DENY", 503)
req = urllib.request.Request(
self.upstream + path,
method=method,
data=body,
headers=dict(headers),
)
try:
with urllib.request.urlopen(req, timeout=self.timeout) as response:
raw = response.read(MAX_RESPONSE + 1)
status = response.status
content_type = response.headers.get("Content-Type", "application/json")
except urllib.error.HTTPError as exc:
raw = exc.read(MAX_RESPONSE + 1)
status = exc.code
content_type = exc.headers.get("Content-Type", "application/json")
except Exception as exc:
raise EdgeError("UPSTREAM_UNAVAILABLE", 502) from exc
if len(raw) > MAX_RESPONSE:
raise EdgeError("UPSTREAM_RESPONSE_TOO_LARGE", 502)
if content_type.split(";", 1)[0].strip().lower() != "application/json":
raise EdgeError("UPSTREAM_CONTENT_TYPE_INVALID", 502)
return status, raw, "application/json"
class EdgeServer(ThreadingHTTPServer):
daemon_threads = True
request_queue_size = 64
allow_reuse_address = True
class Handler(BaseHTTPRequestHandler):
proxy = Proxy()
limiter = RateLimiter()
concurrency = threading.BoundedSemaphore(MAX_CONCURRENT_REQUESTS)
expected_domain = ""
server_version = "AIOSGPTIngressEdge/1.0"
sys_version = ""
def log_message(self, *_args: Any) -> None:
return
def respond_json(self, value: Mapping[str, Any], status: int) -> None:
raw = canonical(value)
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("Referrer-Policy", "no-referrer")
self.send_header("Content-Security-Policy", "default-src 'none'")
self.send_header("Strict-Transport-Security", "max-age=31536000")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def send_proxy_response(self, status: int, raw: bytes, content_type: str) -> None:
self.send_response(status)
self.send_header("Content-Type", content_type)
self.send_header("Cache-Control", "no-store")
self.send_header("X-Content-Type-Options", "nosniff")
self.send_header("Referrer-Policy", "no-referrer")
self.send_header("Content-Security-Policy", "default-src 'none'")
self.send_header("Strict-Transport-Security", "max-age=31536000")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def client_key(self) -> str:
return str(self.client_address[0])
def validate_host(self) -> None:
if not self.expected_domain:
return
host = self.headers.get("Host", "").split(":", 1)[0].lower()
if not hmac.compare_digest(host, self.expected_domain):
raise EdgeError("HOST_HEADER_DENY", 421)
def body(self) -> bytes:
try:
length = int(self.headers.get("Content-Length", "-1"))
except ValueError as exc:
raise EdgeError("CONTENT_LENGTH_INVALID") from exc
if length < 1 or length > MAX_BODY:
raise EdgeError("REQUEST_SIZE_INVALID", 413)
raw = self.rfile.read(length)
if len(raw) != length:
raise EdgeError("REQUEST_BODY_TRUNCATED", 400)
# Validate JSON at the edge so malformed bodies do not consume upstream work.
try:
value = json.loads(raw.decode("utf-8"))
except Exception as exc:
raise EdgeError("REQUEST_JSON_INVALID", 400) from exc
if not isinstance(value, Mapping):
raise EdgeError("REQUEST_JSON_OBJECT_REQUIRED", 400)
return canonical(value)
def handle_proxy(self, method: str) -> None:
client_id = _client_pseudonym(self.client_key())
started = time.monotonic()
acquired = self.concurrency.acquire(blocking=False)
if not acquired:
audit("EDGE_BUSY", client_id=client_id, method=method)
self.respond_json({"error": "EDGE_BUSY"}, 503)
return
label = "unknown"
try:
self.validate_host()
path = allowed_route(method, self.path)
label = route_label(method, path)
if path == "/health" and method == "GET":
self.respond_json(
{
"status": "ok",
"service": "aios-gpt-https-edge",
"upstream": "loopback-readonly-ingress",
},
200,
)
audit(
"EDGE_REQUEST",
client_id=client_id,
method=method,
route=label,
status=200,
duration_ms=int((time.monotonic() - started) * 1000),
)
return
if not self.limiter.allowed(self.client_key()):
audit(
"EDGE_RATE_LIMIT",
client_id=client_id,
method=method,
route=label,
)
self.respond_json({"error": "RATE_LIMITED"}, 429)
return
has_body = method == "POST"
headers = forwarded_headers(self.headers, body=has_body)
body = self.body() if has_body else None
status, raw, content_type = self.proxy.request(
method,
path,
headers,
body,
)
self.send_proxy_response(status, raw, content_type)
audit(
"EDGE_REQUEST",
client_id=client_id,
method=method,
route=label,
status=status,
duration_ms=int((time.monotonic() - started) * 1000),
)
except EdgeError as exc:
audit(
"EDGE_REJECT",
client_id=client_id,
method=method,
route=label,
status=exc.status,
error=exc.code,
)
self.respond_json({"error": exc.code}, exc.status)
finally:
self.concurrency.release()
def do_GET(self) -> None:
self.handle_proxy("GET")
def do_POST(self) -> None:
self.handle_proxy("POST")
def do_PUT(self) -> None:
self.respond_json({"error": "METHOD_DENY"}, 405)
def do_PATCH(self) -> None:
self.respond_json({"error": "METHOD_DENY"}, 405)
def do_DELETE(self) -> None:
self.respond_json({"error": "METHOD_DENY"}, 405)
def do_CONNECT(self) -> None:
self.respond_json({"error": "METHOD_DENY"}, 405)
def do_OPTIONS(self) -> None:
self.respond_json({"error": "METHOD_DENY"}, 405)
def serve() -> None:
host, public_domain = validated_edge_identity(
os.environ.get("AIOS_GPT_EDGE_HOST", "127.0.0.1"),
os.environ.get(PUBLIC_APPROVAL_ENV, "0"),
os.environ.get(PUBLIC_DOMAIN_ENV, ""),
)
port = int(os.environ.get("AIOS_GPT_EDGE_PORT", "18443"))
if not 1024 <= port <= 65535:
raise RuntimeError("GPT_EDGE_PORT_INVALID")
Handler.expected_domain = public_domain
credentials = Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
cert_path = Path(
os.environ.get(
"AIOS_GPT_EDGE_TLS_CERT_FILE",
str(credentials / "tls_cert"),
)
)
key_path = Path(
os.environ.get(
"AIOS_GPT_EDGE_TLS_KEY_FILE",
str(credentials / "tls_key"),
)
)
context = build_tls_context(cert_path, key_path)
server = EdgeServer((host, port), Handler)
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
if __name__ == "__main__":
serve()
@@ -0,0 +1,561 @@
#!/usr/bin/env python3
"""Owner-approved activation/rollback controller for the AIOS GPT HTTPS edge.
This controller never creates approval, TLS, or ingress credentials. It consumes
owner-managed inputs, applies one exact public-bind overlay, verifies the live
edge, and provides an idempotent rollback path.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
import argparse
import hashlib
import json
import os
from pathlib import Path
import re
import socket
import ssl
import stat
import subprocess
import tempfile
import time
import urllib.request
from typing import Any, Mapping
APPROVAL_SCHEMA = "aios.gpt-https-edge.public-approval.v1"
STATE_SCHEMA = "aios.gpt-https-edge.activation-state.v1"
OID_RE = re.compile(r"^[0-9a-f]{40}$")
SHA_RE = re.compile(r"^[0-9a-f]{64}$")
APPROVAL_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$")
DOMAIN_RE = re.compile(
r"(?=.{1,253}\Z)"
r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+"
r"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?"
)
EDGE_UNIT = "aios-gpt-https-edge.service"
INGRESS_UNIT = "aios-gpt-online-task-ingress.service"
PUBLIC_HOST = "0.0.0.0"
PUBLIC_PORT = 18443
INGRESS_HEALTH = "http://127.0.0.1:18945/health"
DROPIN_NAME = "zzzzzzzzzz-owner-approved-public.conf"
class ActivationError(RuntimeError):
pass
def canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def sha256_bytes(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def sha256_file(path: Path) -> str:
return sha256_bytes(path.read_bytes())
def parse_time(value: object, *, field: str) -> datetime:
if not isinstance(value, str) or not value.strip():
raise ActivationError("APPROVAL_" + field.upper() + "_INVALID")
text = value.strip()
if text.endswith("Z"):
text = text[:-1] + "+00:00"
try:
parsed = datetime.fromisoformat(text)
except ValueError as exc:
raise ActivationError("APPROVAL_" + field.upper() + "_INVALID") from exc
if parsed.tzinfo is None:
raise ActivationError("APPROVAL_" + field.upper() + "_INVALID")
return parsed.astimezone(timezone.utc)
def validate_domain(value: object) -> str:
if not isinstance(value, str):
raise ActivationError("APPROVAL_DOMAIN_INVALID")
domain = value.strip().lower()
if DOMAIN_RE.fullmatch(domain) is None:
raise ActivationError("APPROVAL_DOMAIN_INVALID")
return domain
def validate_root_file(
path: Path,
*,
exact_mode: int | None = None,
max_size: int = 256 * 1024,
) -> Path:
if path.is_symlink() or not path.is_file():
raise ActivationError("OWNER_FILE_INVALID")
info = path.stat()
if info.st_uid != 0 or info.st_gid != 0:
raise ActivationError("OWNER_FILE_OWNERSHIP_INVALID")
mode = stat.S_IMODE(info.st_mode)
if exact_mode is not None and mode != exact_mode:
raise ActivationError("OWNER_FILE_MODE_INVALID")
if info.st_size < 16 or info.st_size > max_size:
raise ActivationError("OWNER_FILE_SIZE_INVALID")
return path
def certificate_dns_names(cert_path: Path) -> tuple[str, ...]:
try:
decoded = ssl._ssl._test_decode_cert(str(cert_path))
except Exception as exc:
raise ActivationError("TLS_CERT_DECODE_INVALID") from exc
names = [
value
for kind, value in decoded.get("subjectAltName", ())
if kind == "DNS"
]
if not names:
for rdn in decoded.get("subject", ()):
for key, value in rdn:
if key == "commonName":
names.append(value)
return tuple(names)
def cert_matches_domain(cert_path: Path, domain: str) -> bool:
names = certificate_dns_names(cert_path)
try:
return bool(names) and any(ssl._dnsname_match(name, domain) for name in names)
except ssl.CertificateError:
return False
def validate_tls_pair(
cert_path: Path,
key_path: Path,
domain: str,
*,
now_fn=time.time,
) -> dict[str, Any]:
validate_root_file(cert_path)
validate_root_file(key_path, exact_mode=0o600)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.options |= ssl.OP_NO_COMPRESSION
try:
context.load_cert_chain(str(cert_path), str(key_path))
except Exception as exc:
raise ActivationError("TLS_CERT_KEY_PAIR_INVALID") from exc
if not cert_matches_domain(cert_path, domain):
raise ActivationError("TLS_CERT_DOMAIN_MISMATCH")
decoded = ssl._ssl._test_decode_cert(str(cert_path))
not_after = decoded.get("notAfter")
if not not_after:
raise ActivationError("TLS_CERT_EXPIRY_MISSING")
remaining = ssl.cert_time_to_seconds(not_after) - now_fn()
if remaining < 7 * 86400:
raise ActivationError("TLS_CERT_EXPIRY_TOO_CLOSE")
return {
"cert_sha256": sha256_file(cert_path),
"remaining_days": int(remaining // 86400),
}
@dataclass(frozen=True)
class Approval:
approval_id: str
candidate_oid: str
domain: str
bind_host: str
port: int
tls_cert_sha256: str
owner: str
issued_at: datetime
not_before: datetime
expires_at: datetime
raw_sha256: str
@classmethod
def load(
cls,
path: Path,
*,
candidate_oid: str,
cert_path: Path,
now: datetime | None = None,
) -> "Approval":
validate_root_file(path, exact_mode=0o600, max_size=64 * 1024)
try:
value = json.loads(path.read_text(encoding="utf-8"))
except Exception as exc:
raise ActivationError("APPROVAL_JSON_INVALID") from exc
if not isinstance(value, Mapping):
raise ActivationError("APPROVAL_JSON_INVALID")
allowed = {
"schema",
"approval_id",
"candidate_oid",
"domain",
"public_bind",
"bind_host",
"port",
"tls_cert_sha256",
"issued_at",
"not_before",
"expires_at",
"owner",
}
if set(value) != allowed or value.get("schema") != APPROVAL_SCHEMA:
raise ActivationError("APPROVAL_FIELDS_INVALID")
approval_id = value.get("approval_id")
if not isinstance(approval_id, str) or APPROVAL_ID_RE.fullmatch(approval_id) is None:
raise ActivationError("APPROVAL_ID_INVALID")
if not OID_RE.fullmatch(str(value.get("candidate_oid") or "")):
raise ActivationError("APPROVAL_CANDIDATE_INVALID")
if value["candidate_oid"] != candidate_oid:
raise ActivationError("APPROVAL_CANDIDATE_MISMATCH")
if value.get("public_bind") is not True:
raise ActivationError("APPROVAL_PUBLIC_BIND_REQUIRED")
if value.get("bind_host") != PUBLIC_HOST or value.get("port") != PUBLIC_PORT:
raise ActivationError("APPROVAL_BINDING_INVALID")
cert_sha = str(value.get("tls_cert_sha256") or "")
if SHA_RE.fullmatch(cert_sha) is None:
raise ActivationError("APPROVAL_CERT_SHA_INVALID")
if not cert_path.is_file() or sha256_file(cert_path) != cert_sha:
raise ActivationError("APPROVAL_CERT_SHA_MISMATCH")
domain = validate_domain(value.get("domain"))
owner = value.get("owner")
if not isinstance(owner, str) or not owner.strip() or len(owner) > 255:
raise ActivationError("APPROVAL_OWNER_INVALID")
issued_at = parse_time(value.get("issued_at"), field="issued_at")
not_before = parse_time(value.get("not_before"), field="not_before")
expires_at = parse_time(value.get("expires_at"), field="expires_at")
current = now or datetime.now(timezone.utc)
if not (issued_at <= not_before <= expires_at):
raise ActivationError("APPROVAL_TIME_ORDER_INVALID")
if current < not_before:
raise ActivationError("APPROVAL_NOT_YET_VALID")
if current >= expires_at:
raise ActivationError("APPROVAL_EXPIRED")
if expires_at - issued_at > __import__("datetime").timedelta(days=7):
raise ActivationError("APPROVAL_VALIDITY_TOO_LONG")
return cls(
approval_id=approval_id,
candidate_oid=candidate_oid,
domain=domain,
bind_host=PUBLIC_HOST,
port=PUBLIC_PORT,
tls_cert_sha256=cert_sha,
owner=owner.strip(),
issued_at=issued_at,
not_before=not_before,
expires_at=expires_at,
raw_sha256=sha256_file(path),
)
def validate_release(release_root: Path) -> dict[str, Path]:
if release_root.is_symlink() or not release_root.is_dir():
raise ActivationError("IMMUTABLE_RELEASE_INVALID")
edge = release_root / "active-runtime/app/aios_gpt_https_edge.py"
controller = release_root / "active-runtime/app/aios_gpt_https_edge_activation.py"
for path in (edge, controller):
if path.is_symlink() or not path.is_file():
raise ActivationError("IMMUTABLE_RELEASE_INVALID")
return {"edge": edge, "controller": controller}
def render_dropin(approval: Approval, release_root: Path) -> str:
app = release_root / "active-runtime/app/aios_gpt_https_edge.py"
return (
"[Service]\n"
f"Environment=AIOS_GPT_EDGE_HOST={approval.bind_host}\n"
f"Environment=AIOS_GPT_EDGE_PORT={approval.port}\n"
"Environment=AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=1\n"
f"Environment=AIOS_GPT_EDGE_PUBLIC_DOMAIN={approval.domain}\n"
"ExecStart=\n"
f"ExecStart=/usr/bin/python3 -B {app}\n"
f"ReadOnlyPaths={release_root}\n"
)
def atomic_write(path: Path, data: str, mode: int = 0o600) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix="." + path.name + ".", dir=path.parent)
try:
os.fchmod(fd, mode)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
handle.write(data)
handle.flush()
os.fsync(handle.fileno())
os.replace(name, path)
finally:
if os.path.exists(name):
os.unlink(name)
class Systemd:
def run(self, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(
["/usr/bin/systemctl", *args],
check=check,
text=True,
capture_output=True,
timeout=30,
)
def active(self, unit: str) -> bool:
return self.run("is-active", "--quiet", unit, check=False).returncode == 0
def enabled(self, unit: str) -> bool:
return self.run("is-enabled", "--quiet", unit, check=False).returncode == 0
def show(self, unit: str, prop: str) -> str:
return self.run("show", unit, f"--property={prop}", "--value").stdout.strip()
def require_ingress_ready(systemd: Systemd) -> None:
if not systemd.active(INGRESS_UNIT):
raise ActivationError("INGRESS_SERVICE_NOT_ACTIVE")
try:
with urllib.request.urlopen(INGRESS_HEALTH, timeout=5) as response:
if response.status != 200:
raise ActivationError("INGRESS_HEALTH_FAILED")
value = json.loads(response.read().decode())
except ActivationError:
raise
except Exception as exc:
raise ActivationError("INGRESS_HEALTH_FAILED") from exc
if value.get("status") != "ok":
raise ActivationError("INGRESS_HEALTH_FAILED")
def tls_probe(domain: str, cert_path: Path, *, port: int = PUBLIC_PORT) -> None:
context = ssl.create_default_context(cafile=str(cert_path))
context.check_hostname = True
context.verify_mode = ssl.CERT_REQUIRED
try:
with socket.create_connection(("127.0.0.1", port), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=domain) as tls_sock:
if tls_sock.version() not in {"TLSv1.2", "TLSv1.3"}:
raise ActivationError("EDGE_TLS_VERSION_INVALID")
except ActivationError:
raise
except Exception as exc:
raise ActivationError("EDGE_TLS_PROBE_FAILED") from exc
def receipt_for(
approval: Approval,
release_root: Path,
*,
state: str,
) -> dict[str, Any]:
return {
"schema": STATE_SCHEMA,
"state": state,
"approval_id": approval.approval_id,
"approval_sha256": approval.raw_sha256,
"candidate_oid": approval.candidate_oid,
"domain": approval.domain,
"bind_host": approval.bind_host,
"port": approval.port,
"tls_cert_sha256": approval.tls_cert_sha256,
"release_root": str(release_root),
"updated_at": datetime.now(timezone.utc).isoformat(),
}
def activate(
*,
approval: Approval,
release_root: Path,
cert_path: Path,
key_path: Path,
dropin_path: Path,
state_path: Path,
systemd: Systemd,
) -> dict[str, Any]:
if os.geteuid() != 0:
raise ActivationError("ROOT_REQUIRED")
release = validate_release(release_root)
app = release["edge"]
require_ingress_ready(systemd)
validate_tls_pair(cert_path, key_path, approval.domain)
if state_path.is_file():
try:
existing = json.loads(state_path.read_text(encoding="utf-8"))
except Exception:
existing = {}
if (
existing.get("schema") == STATE_SCHEMA
and existing.get("state") == "ACTIVE"
and existing.get("approval_sha256") == approval.raw_sha256
and existing.get("candidate_oid") == approval.candidate_oid
and systemd.active(EDGE_UNIT)
):
return existing
dropin = render_dropin(approval, release_root)
modified = False
try:
atomic_write(dropin_path, dropin, mode=0o644)
modified = True
systemd.run("daemon-reload")
systemd.run("enable", EDGE_UNIT)
systemd.run("restart", EDGE_UNIT)
if not systemd.active(EDGE_UNIT):
raise ActivationError("EDGE_SERVICE_NOT_ACTIVE")
exec_start = systemd.show(EDGE_UNIT, "ExecStart")
environment = systemd.show(EDGE_UNIT, "Environment")
if str(app) not in exec_start:
raise ActivationError("EDGE_EFFECTIVE_EXECSTART_INVALID")
for expected in (
f"AIOS_GPT_EDGE_HOST={approval.bind_host}",
f"AIOS_GPT_EDGE_PORT={approval.port}",
"AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=1",
f"AIOS_GPT_EDGE_PUBLIC_DOMAIN={approval.domain}",
):
if expected not in environment:
raise ActivationError("EDGE_EFFECTIVE_ENVIRONMENT_INVALID")
tls_probe(approval.domain, cert_path, port=approval.port)
receipt = receipt_for(approval, release_root, state="ACTIVE")
atomic_write(state_path, json.dumps(receipt, indent=2, sort_keys=True) + "\n")
return receipt
except Exception:
if modified:
rollback(
dropin_path=dropin_path,
state_path=state_path,
systemd=systemd,
reason="activation_failed",
)
raise
def rollback(
*,
dropin_path: Path,
state_path: Path,
systemd: Systemd,
reason: str,
) -> dict[str, Any]:
if os.geteuid() != 0:
raise ActivationError("ROOT_REQUIRED")
systemd.run("stop", EDGE_UNIT, check=False)
systemd.run("disable", EDGE_UNIT, check=False)
dropin_path.unlink(missing_ok=True)
systemd.run("daemon-reload")
if systemd.active(EDGE_UNIT):
raise ActivationError("EDGE_ROLLBACK_SERVICE_STILL_ACTIVE")
previous: dict[str, Any] = {}
if state_path.is_file():
try:
previous = json.loads(state_path.read_text(encoding="utf-8"))
except Exception:
previous = {}
receipt = {
"schema": STATE_SCHEMA,
"state": "ROLLED_BACK",
"reason": reason[:255],
"candidate_oid": previous.get("candidate_oid"),
"approval_id": previous.get("approval_id"),
"domain": previous.get("domain"),
"updated_at": datetime.now(timezone.utc).isoformat(),
}
atomic_write(state_path, json.dumps(receipt, indent=2, sort_keys=True) + "\n")
return receipt
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("command", choices=("check", "activate", "rollback"))
parser.add_argument("--candidate")
parser.add_argument("--approval", default="/etc/aios-gitops/gpt-https-edge.approved")
parser.add_argument("--tls-cert", default="/etc/aios-gitops/gpt-https-edge-tls.crt")
parser.add_argument("--tls-key", default="/etc/aios-gitops/gpt-https-edge-tls.key")
parser.add_argument("--release-root")
parser.add_argument(
"--dropin",
default=f"/etc/systemd/system/{EDGE_UNIT}.d/{DROPIN_NAME}",
)
parser.add_argument(
"--state",
default="/var/lib/aios-gpt-https-edge-activation/state.json",
)
parser.add_argument("--reason", default="owner_requested")
args = parser.parse_args()
systemd = Systemd()
if args.command == "rollback":
value = rollback(
dropin_path=Path(args.dropin),
state_path=Path(args.state),
systemd=systemd,
reason=args.reason,
)
print(json.dumps(value, indent=2, sort_keys=True))
return 0
candidate = str(args.candidate or "")
if OID_RE.fullmatch(candidate) is None:
raise SystemExit("CANDIDATE_REQUIRED")
release_root = (
Path(args.release_root)
if args.release_root
else Path(f"/opt/aios/releases/{candidate}-gpt-https-edge-public")
)
cert_path = Path(args.tls_cert)
key_path = Path(args.tls_key)
approval = Approval.load(
Path(args.approval),
candidate_oid=candidate,
cert_path=cert_path,
)
tls = validate_tls_pair(cert_path, key_path, approval.domain)
release = validate_release(release_root)
require_ingress_ready(systemd)
if args.command == "check":
value = {
"status": "READY",
"candidate_oid": candidate,
"approval_id": approval.approval_id,
"approval_sha256": approval.raw_sha256,
"domain": approval.domain,
"bind_host": approval.bind_host,
"port": approval.port,
"edge_sha256": sha256_file(release["edge"]),
"controller_sha256": sha256_file(release["controller"]),
**tls,
"public_network_mutation": False,
}
print(json.dumps(value, indent=2, sort_keys=True))
return 0
value = activate(
approval=approval,
release_root=release_root,
cert_path=cert_path,
key_path=key_path,
dropin_path=Path(args.dropin),
state_path=Path(args.state),
systemd=systemd,
)
print(json.dumps(value, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,816 @@
#!/usr/bin/env python3
"""Credential-isolated host-side ingress for externally scheduled read-only Online Tasks.
This service is intentionally not a public AIOS control plane. It accepts a tiny,
authenticated external intent and translates it to the canonical loopback Bridge.
Callers cannot select repository coordinates, capabilities, execution action,
worker, branch policy, credentials, verification decisions, or write permissions.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
import hashlib
import hmac
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import os
from pathlib import Path
import re
import sqlite3
import threading
import urllib.error
import urllib.request
from typing import Any, Mapping
SCHEMA = "aios.gpt-online-task-ingress.request.v1"
RESPONSE_SCHEMA = "aios.gpt-online-task-ingress.response.v1"
OPENAPI_VERSION = "3.1.0"
REQUEST_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$")
TASK_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
JOB_ID_RE = re.compile(r"^job-[0-9a-f]{32}$")
OID_RE = re.compile(r"^[0-9a-f]{40}$")
MAX_BODY = 64 * 1024
MAX_GOAL = 12_000
MAX_INSTRUCTIONS = 24_000
MAX_CRITERIA = 16
MAX_CRITERION_TEXT = 1_000
READONLY_CAPABILITIES = ("git.read", "git.test")
REPOSITORY = "aios/aios"
class IngressError(RuntimeError):
def __init__(self, code: str, status: int = 400) -> None:
super().__init__(code)
self.code = code
self.status = status
def canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def digest(value: object) -> str:
return hashlib.sha256(canonical(value)).hexdigest()
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def load_secret(path: Path, *, code: str) -> str:
if path.is_symlink() or not path.is_file():
raise RuntimeError(code + "_UNAVAILABLE")
value = path.read_text(encoding="utf-8").strip()
if len(value) < 32 or "\n" in value or "\r" in value:
raise RuntimeError(code + "_INVALID")
return value
def normalized_request(value: Mapping[str, Any]) -> dict[str, Any]:
allowed = {
"schema",
"request_id",
"goal",
"instructions",
"success_criteria",
"constraints",
}
if set(value) - allowed:
raise IngressError("UNKNOWN_FIELDS")
if value.get("schema") != SCHEMA:
raise IngressError("SCHEMA_INVALID")
request_id = value.get("request_id")
if not isinstance(request_id, str) or REQUEST_ID_RE.fullmatch(request_id) is None:
raise IngressError("REQUEST_ID_INVALID")
goal = value.get("goal")
if not isinstance(goal, str) or not goal.strip() or len(goal) > MAX_GOAL:
raise IngressError("GOAL_INVALID")
instructions = value.get("instructions", "")
if not isinstance(instructions, str) or len(instructions) > MAX_INSTRUCTIONS:
raise IngressError("INSTRUCTIONS_INVALID")
criteria = value.get("success_criteria") or []
if not isinstance(criteria, list) or len(criteria) > MAX_CRITERIA:
raise IngressError("SUCCESS_CRITERIA_INVALID")
normalized_criteria = []
for item in criteria:
if not isinstance(item, Mapping) or set(item) != {"id", "description"}:
raise IngressError("SUCCESS_CRITERIA_INVALID")
criterion_id = item.get("id")
description = item.get("description")
if (
not isinstance(criterion_id, str)
or not criterion_id.strip()
or len(criterion_id) > 120
or not isinstance(description, str)
or not description.strip()
or len(description) > MAX_CRITERION_TEXT
):
raise IngressError("SUCCESS_CRITERIA_INVALID")
normalized_criteria.append(
{"id": criterion_id.strip(), "description": description.strip()}
)
constraints = value.get("constraints") or {}
if not isinstance(constraints, Mapping):
raise IngressError("CONSTRAINTS_INVALID")
allowed_constraints = {
"max_files",
"focus_paths",
"notes",
}
if set(constraints) - allowed_constraints:
raise IngressError("CONSTRAINTS_FIELDS_INVALID")
normalized_constraints: dict[str, Any] = {}
if "max_files" in constraints:
max_files = constraints["max_files"]
if (
not isinstance(max_files, int)
or isinstance(max_files, bool)
or not 1 <= max_files <= 500
):
raise IngressError("MAX_FILES_INVALID")
normalized_constraints["max_files"] = max_files
if "focus_paths" in constraints:
paths = constraints["focus_paths"]
if (
not isinstance(paths, list)
or len(paths) > 32
or not all(
isinstance(path, str)
and path
and len(path) <= 512
and not path.startswith("/")
and ".." not in Path(path).parts
for path in paths
)
):
raise IngressError("FOCUS_PATHS_INVALID")
normalized_constraints["focus_paths"] = paths
if "notes" in constraints:
notes = constraints["notes"]
if not isinstance(notes, str) or len(notes) > 4_000:
raise IngressError("CONSTRAINT_NOTES_INVALID")
normalized_constraints["notes"] = notes
return {
"schema": SCHEMA,
"request_id": request_id,
"goal": goal.strip(),
"instructions": instructions,
"success_criteria": normalized_criteria,
"constraints": normalized_constraints,
}
def validated_bind_host(value: str) -> str:
host = value.strip()
if host not in {"127.0.0.1", "::1", "localhost"}:
raise RuntimeError("GPT_INGRESS_NON_LOOPBACK_BIND_DENY")
return host
def task_id_for(request_id: str) -> str:
suffix = hashlib.sha256(request_id.encode("utf-8")).hexdigest()[:24]
return "gpt-readonly-" + suffix
def branch_for(task_id: str) -> str:
return "agent/external-gpt-readonly/" + task_id[-24:]
@dataclass(frozen=True)
class Upstream:
bridge_base: str
task_base: str
control_token: str
timeout: int = 20
def _request(
self,
base: str,
method: str,
path: str,
body: Mapping[str, Any] | None = None,
) -> tuple[int, dict[str, Any]]:
raw_body = None if body is None else canonical(body)
headers = {
"Authorization": "Bearer " + self.control_token,
"Accept": "application/json",
}
if raw_body is not None:
headers["Content-Type"] = "application/json"
req = urllib.request.Request(
base.rstrip("/") + path,
method=method,
data=raw_body,
headers=headers,
)
try:
with urllib.request.urlopen(req, timeout=self.timeout) as response:
raw = response.read()
status = response.status
except urllib.error.HTTPError as exc:
raw = exc.read()
status = exc.code
except Exception as exc:
raise IngressError("UPSTREAM_TRANSPORT_FAILED", 503) from exc
try:
value = json.loads(raw.decode("utf-8"))
except Exception as exc:
raise IngressError("UPSTREAM_RESPONSE_INVALID", 502) from exc
if not isinstance(value, Mapping):
raise IngressError("UPSTREAM_RESPONSE_INVALID", 502)
return status, dict(value)
def control_snapshot(self) -> dict[str, Any]:
status, value = self._request(
self.bridge_base,
"GET",
"/v2/control-plane-snapshot",
)
if status != 200:
raise IngressError("CONTROL_SNAPSHOT_UNAVAILABLE", 503)
return value
def create_governed_task(
self,
body: Mapping[str, Any],
) -> tuple[int, dict[str, Any]]:
return self._request(
self.bridge_base,
"POST",
"/v2/governed-task",
body,
)
def execution_envelope(
self,
body: Mapping[str, Any],
) -> tuple[int, dict[str, Any]]:
return self._request(
self.bridge_base,
"POST",
"/v2/execution-envelope",
body,
)
def get_task(self, task_id: str) -> tuple[int, dict[str, Any]]:
return self._request(
self.task_base,
"GET",
"/v1/tasks/" + task_id,
)
class Store:
def __init__(self, path: Path) -> None:
self.path = Path(path)
self._lock = threading.RLock()
self.path.parent.mkdir(parents=True, exist_ok=True)
with self._connect() as con:
con.execute(
"""
CREATE TABLE IF NOT EXISTS external_task_requests(
request_id TEXT PRIMARY KEY,
request_sha256 TEXT NOT NULL,
task_id TEXT NOT NULL UNIQUE,
job_id TEXT,
base_oid TEXT,
state TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
response_json TEXT
)
"""
)
con.commit()
def _connect(self) -> sqlite3.Connection:
con = sqlite3.connect(self.path, timeout=30)
con.row_factory = sqlite3.Row
con.execute("PRAGMA busy_timeout=30000")
return con
def get(self, request_id: str) -> sqlite3.Row | None:
with self._connect() as con:
return con.execute(
"SELECT * FROM external_task_requests WHERE request_id=?",
(request_id,),
).fetchone()
def reserve(self, request: Mapping[str, Any], task_id: str) -> sqlite3.Row:
request_sha = digest(request)
timestamp = now()
with self._lock, self._connect() as con:
con.execute("BEGIN IMMEDIATE")
row = con.execute(
"SELECT * FROM external_task_requests WHERE request_id=?",
(request["request_id"],),
).fetchone()
if row is not None:
if row["request_sha256"] != request_sha:
raise IngressError("IDEMPOTENCY_CONFLICT", 409)
con.commit()
return row
con.execute(
"""
INSERT INTO external_task_requests(
request_id,request_sha256,task_id,state,created_at,updated_at
) VALUES(?,?,?,?,?,?)
""",
(
request["request_id"],
request_sha,
task_id,
"PENDING",
timestamp,
timestamp,
),
)
con.commit()
row = self.get(request["request_id"])
assert row is not None
return row
def bound(
self,
request_id: str,
*,
job_id: str,
base_oid: str,
response: Mapping[str, Any],
) -> None:
with self._lock, self._connect() as con:
con.execute(
"""
UPDATE external_task_requests
SET job_id=?,base_oid=?,state='BOUND',response_json=?,updated_at=?
WHERE request_id=?
""",
(
job_id,
base_oid,
json.dumps(response, sort_keys=True, separators=(",", ":")),
now(),
request_id,
),
)
con.commit()
class IngressService:
def __init__(self, upstream: Upstream, store: Store) -> None:
self.upstream = upstream
self.store = store
@staticmethod
def _main_oid(snapshot: Mapping[str, Any]) -> str:
candidates = [
snapshot.get("authoritative_head_oid"),
snapshot.get("head"),
(snapshot.get("aggregate") or {}).get("authoritative_head_oid")
if isinstance(snapshot.get("aggregate"), Mapping)
else None,
]
for candidate in candidates:
if isinstance(candidate, str) and OID_RE.fullmatch(candidate):
return candidate
raise IngressError("AUTHORITATIVE_HEAD_UNAVAILABLE", 503)
@staticmethod
def _public_response(
request_id: str,
task_id: str,
job_id: str,
state: str,
) -> dict[str, Any]:
return {
"schema": RESPONSE_SCHEMA,
"request_id": request_id,
"task_id": task_id,
"job_id": job_id,
"state": state,
"execution_mode": "source_capture",
"capabilities": list(READONLY_CAPABILITIES),
"write_access": False,
"official_verification_required": True,
}
def _recover_existing(
self,
request: Mapping[str, Any],
row: sqlite3.Row,
) -> dict[str, Any] | None:
task_id = str(row["task_id"])
status, task = self.upstream.get_task(task_id)
if status == 404:
return None
if status != 200:
raise IngressError("TASK_RECOVERY_FAILED", 503)
metadata = task.get("metadata")
execution = (
metadata.get("execution")
if isinstance(metadata, Mapping)
and isinstance(metadata.get("execution"), Mapping)
else {}
)
job_id = str(execution.get("job_id") or row["job_id"] or "")
base_oid = str(execution.get("base_oid") or row["base_oid"] or "")
if (
task.get("task_id") != task_id
or not JOB_ID_RE.fullmatch(job_id)
or not OID_RE.fullmatch(base_oid)
or execution.get("action") != "source_capture"
or sorted(execution.get("capability_scope") or [])
!= sorted(READONLY_CAPABILITIES)
or (execution.get("governance") or {}).get("writer_lease") is not False
):
raise IngressError("TASK_RECOVERY_BINDING_INVALID", 409)
response = self._public_response(
request["request_id"],
task_id,
job_id,
str(task.get("status") or "BOUND"),
)
self.store.bound(
request["request_id"],
job_id=job_id,
base_oid=base_oid,
response=response,
)
return response
def create(self, raw: Mapping[str, Any]) -> dict[str, Any]:
request = normalized_request(raw)
task_id = task_id_for(request["request_id"])
row = self.store.reserve(request, task_id)
if row["state"] == "BOUND" and row["response_json"]:
return json.loads(row["response_json"])
recovered = self._recover_existing(request, row)
if recovered is not None:
return recovered
snapshot = self.upstream.control_snapshot()
base_oid = self._main_oid(snapshot)
target_branch = branch_for(task_id)
binding_key = "gpt-readonly-binding-" + digest(
{"request_id": request["request_id"], "base_oid": base_oid}
)[:24]
constraints = {
**request["constraints"],
"repository_mutation": "forbidden",
"control_plane_side_effects": "forbidden",
"credential_access": "forbidden",
}
body = {
"task_id": task_id,
"repository": REPOSITORY,
"base_oid": base_oid,
"expected_branch_oid": base_oid,
"target_branch": target_branch,
"branch_mode": "CREATE_NEW",
"generation": 1,
"risk": "low",
"description": "Externally requested read-only governed Online Task.",
"idempotency_key": binding_key,
"priority": 0,
"max_attempts": 3,
"require_online_task_projection": True,
"online_task": {
"goal": request["goal"],
"instructions": request["instructions"],
"success_criteria": request["success_criteria"],
"constraints": constraints,
"required_capabilities": list(READONLY_CAPABILITIES),
"priority": 0,
"max_attempts": 3,
},
"execution": {
"action": "source_capture",
"adapter": "source_capture",
"capability_scope": list(READONLY_CAPABILITIES),
"source_signals": [
"external read-only source analysis request",
"no repository mutation no credential access no control-plane side effects",
],
},
}
status, value = self.upstream.create_governed_task(body)
if status not in {200, 201, 202}:
if status == 409:
recovered = self._recover_existing(request, row)
if recovered is not None:
return recovered
raise IngressError(
"GOVERNED_TASK_CREATE_FAILED",
409 if status == 409 else 503,
)
job_id = str(value.get("job_id") or "")
if value.get("task_id") != task_id or JOB_ID_RE.fullmatch(job_id) is None:
raise IngressError("GOVERNED_TASK_RESPONSE_INVALID", 502)
envelope_status, envelope = self.upstream.execution_envelope(
{
"task_id": task_id,
"job_id": job_id,
"required_capabilities": list(READONLY_CAPABILITIES),
"execution": body["execution"],
}
)
if envelope_status != 200:
raise IngressError("EXECUTION_PROJECTION_FAILED", 503)
if (
envelope.get("task_id") != task_id
or envelope.get("job_id") != job_id
or envelope.get("base_oid") != base_oid
or envelope.get("action") != "source_capture"
or sorted(envelope.get("capability_scope") or [])
!= sorted(READONLY_CAPABILITIES)
or (envelope.get("governance") or {}).get("writer_lease") is not False
):
raise IngressError("EXECUTION_PROJECTION_INVALID", 502)
response = self._public_response(
request["request_id"],
task_id,
job_id,
"BOUND",
)
self.store.bound(
request["request_id"],
job_id=job_id,
base_oid=base_oid,
response=response,
)
return response
def status(self, request_id: str) -> dict[str, Any]:
if REQUEST_ID_RE.fullmatch(request_id) is None:
raise IngressError("REQUEST_ID_INVALID")
row = self.store.get(request_id)
if row is None:
raise IngressError("REQUEST_NOT_FOUND", 404)
task_id = str(row["task_id"])
status, task = self.upstream.get_task(task_id)
if status == 200 and not row["job_id"]:
metadata = task.get("metadata")
execution = (
metadata.get("execution")
if isinstance(metadata, Mapping)
and isinstance(metadata.get("execution"), Mapping)
else {}
)
job_id = str(execution.get("job_id") or "")
base_oid = str(execution.get("base_oid") or "")
if (
JOB_ID_RE.fullmatch(job_id)
and OID_RE.fullmatch(base_oid)
and execution.get("action") == "source_capture"
and sorted(execution.get("capability_scope") or [])
== sorted(READONLY_CAPABILITIES)
and (execution.get("governance") or {}).get("writer_lease") is False
):
recovered = self._public_response(
request_id,
task_id,
job_id,
str(task.get("status") or "BOUND"),
)
self.store.bound(
request_id,
job_id=job_id,
base_oid=base_oid,
response=recovered,
)
row = self.store.get(request_id) or row
if status == 404:
return {
"schema": RESPONSE_SCHEMA,
"request_id": request_id,
"task_id": task_id,
"state": "PENDING",
"official_verification_required": True,
}
if status != 200:
raise IngressError("TASK_STATUS_UNAVAILABLE", 503)
result = task.get("result") if isinstance(task.get("result"), Mapping) else {}
verification = (
task.get("verification")
if isinstance(task.get("verification"), Mapping)
else {}
)
safe = {
"schema": RESPONSE_SCHEMA,
"request_id": request_id,
"task_id": task_id,
"job_id": str(row["job_id"] or ""),
"state": str(task.get("status") or "UNKNOWN"),
"attempt": int(task.get("attempt") or 0),
"claimed": bool(task.get("claimed_by")),
"result_status": result.get("status"),
"result_classification": result.get("classification"),
"official_verification": verification.get("decision"),
"official_classification": verification.get("classification"),
"write_access": False,
}
if result.get("execution_mode") == "source_capture":
snapshot = result.get("source_snapshot")
if isinstance(snapshot, Mapping):
safe["source_snapshot_sha256"] = snapshot.get("payload_sha256")
safe["source_snapshot_file_count"] = snapshot.get("file_count")
return safe
def openapi_document() -> dict[str, Any]:
return {
"openapi": OPENAPI_VERSION,
"info": {
"title": "AIOS Read-Only Online Task Ingress",
"version": "1.0.0",
},
"paths": {
"/v1/read-only-tasks": {
"post": {
"operationId": "createReadOnlyOnlineTask",
"security": [{"bearerAuth": []}],
"requestBody": {"required": True},
"responses": {"200": {"description": "Task accepted"}},
}
},
"/v1/read-only-tasks/{request_id}": {
"get": {
"operationId": "getReadOnlyOnlineTaskStatus",
"security": [{"bearerAuth": []}],
"responses": {"200": {"description": "Sanitized task status"}},
}
},
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
}
}
},
}
class Handler(BaseHTTPRequestHandler):
service: IngressService | None = None
ingress_token: str = ""
def log_message(self, *_args: Any) -> None:
return
def respond(self, value: Mapping[str, Any], status: int) -> None:
raw = canonical(value)
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
def authorized(self) -> bool:
header = self.headers.get("Authorization", "")
return (
header.startswith("Bearer ")
and bool(self.ingress_token)
and hmac.compare_digest(header[7:], self.ingress_token)
)
def body(self) -> dict[str, Any]:
try:
length = int(self.headers.get("Content-Length", "-1"))
except ValueError as exc:
raise IngressError("CONTENT_LENGTH_INVALID") from exc
if length < 1 or length > MAX_BODY:
raise IngressError("REQUEST_SIZE_INVALID", 413)
try:
value = json.loads(self.rfile.read(length).decode("utf-8"))
except Exception as exc:
raise IngressError("REQUEST_JSON_INVALID") from exc
if not isinstance(value, Mapping):
raise IngressError("REQUEST_OBJECT_REQUIRED")
return dict(value)
def do_GET(self) -> None:
if self.path == "/health":
self.respond(
{
"status": "ok",
"service": "aios-gpt-online-task-ingress",
"mode": "read-only",
},
200,
)
return
if self.path == "/openapi.json":
self.respond(openapi_document(), 200)
return
if not self.authorized():
self.respond({"error": "UNAUTHORIZED"}, 401)
return
match = re.fullmatch(r"/v1/read-only-tasks/([^/?#]+)", self.path)
if match is None:
self.respond({"error": "NOT_FOUND"}, 404)
return
try:
if self.service is None:
raise IngressError("RUNTIME_UNAVAILABLE", 503)
self.respond(self.service.status(match.group(1)), 200)
except IngressError as exc:
self.respond({"error": exc.code}, exc.status)
def do_POST(self) -> None:
if self.path != "/v1/read-only-tasks":
self.respond({"error": "NOT_FOUND"}, 404)
return
if not self.authorized():
self.respond({"error": "UNAUTHORIZED"}, 401)
return
try:
if self.service is None:
raise IngressError("RUNTIME_UNAVAILABLE", 503)
value = self.service.create(self.body())
self.respond(value, 200)
except IngressError as exc:
self.respond({"error": exc.code}, exc.status)
def serve() -> None:
ingress_token_path = Path(
os.environ.get(
"AIOS_GPT_INGRESS_TOKEN_FILE",
str(
Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
/ "gpt_ingress_token"
),
)
)
control_token_path = Path(
os.environ.get(
"AIOS_AGENT_TASK_CONTROL_TOKEN_FILE",
str(
Path(os.environ.get("CREDENTIALS_DIRECTORY", "/run/credentials"))
/ "agent_task_control_token"
),
)
)
ingress_token = load_secret(
ingress_token_path,
code="GPT_INGRESS_CREDENTIAL",
)
control_token = load_secret(
control_token_path,
code="CONTROL_PLANE_CREDENTIAL",
)
state_root = Path(
os.environ.get(
"AIOS_GPT_INGRESS_STATE_ROOT",
"/var/lib/aios-gpt-online-task-ingress",
)
)
host = validated_bind_host(
os.environ.get("AIOS_GPT_INGRESS_HOST", "127.0.0.1")
)
port = int(os.environ.get("AIOS_GPT_INGRESS_PORT", "18945"))
if not 1024 <= port <= 65535:
raise RuntimeError("GPT_INGRESS_PORT_INVALID")
Handler.service = IngressService(
Upstream(
bridge_base=os.environ.get(
"AIOS_GOVERNED_WRITE_API_URL",
"http://127.0.0.1:18892",
),
task_base=os.environ.get(
"AIOS_AGENT_TASK_API_URL",
"http://127.0.0.1:18940",
),
control_token=control_token,
),
Store(state_root / "state.sqlite3"),
)
Handler.ingress_token = ingress_token
ThreadingHTTPServer((host, port), Handler).serve_forever()
if __name__ == "__main__":
serve()
+377 -24
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','')
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):
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
try:
rec=authenticate(tok)
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 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,
},
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,232 @@
#!/usr/bin/env python3
"""Root-owned Model Governance configuration reconciler.
The config contains provider/model/policy metadata only. Raw credentials are rejected by
aios_model_governance and never stored in the governance database.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Any, Mapping
from aios_model_governance import (
ModelGovernanceError,
ModelGovernanceStore,
ModelRecord,
ProviderRecord,
ScopePolicy,
)
SCHEMA = "aios.model-governance.bootstrap.v1"
MAX_CONFIG_BYTES = 512 * 1024
def canonical(value: object) -> bytes:
return json.dumps(
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
).encode("utf-8")
def fail(code: str) -> None:
raise SystemExit(code)
def _load(path: Path) -> dict[str, Any]:
if path.is_symlink() or not path.is_file() or path.stat().st_size > MAX_CONFIG_BYTES:
fail("MODEL_GOVERNANCE_CONFIG_INVALID")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError:
fail("MODEL_GOVERNANCE_CONFIG_INVALID")
if not isinstance(value, Mapping) or value.get("schema") != SCHEMA:
fail("MODEL_GOVERNANCE_CONFIG_SCHEMA_INVALID")
return dict(value)
def _provider_desired(value: Mapping[str, Any]) -> ProviderRecord:
return ProviderRecord(
provider_id=str(value.get("provider_id") or ""),
credential_ref=value.get("credential_ref"),
enabled=bool(value.get("enabled", True)),
failure_domain=str(value.get("failure_domain") or "default"),
max_failures=int(value.get("max_failures") or 3),
cooldown_seconds=int(value.get("cooldown_seconds") or 60),
)
def _model_desired(value: Mapping[str, Any]) -> ModelRecord:
return ModelRecord(
model_id=str(value.get("model_id") or ""),
provider_id=str(value.get("provider_id") or ""),
version=str(value.get("version") or ""),
capabilities=tuple(value.get("capabilities") or ()),
input_cost_micros_per_1k=int(value.get("input_cost_micros_per_1k") or 0),
output_cost_micros_per_1k=int(value.get("output_cost_micros_per_1k") or 0),
max_input_tokens=int(value.get("max_input_tokens") or 0),
max_output_tokens=int(value.get("max_output_tokens") or 0),
evaluation_state=str(value.get("evaluation_state") or "APPROVED"),
evaluation_sha256=str(value.get("evaluation_sha256") or ""),
enabled=bool(value.get("enabled", True)),
priority=int(value.get("priority") or 100),
)
def _policy_desired(value: Mapping[str, Any]) -> ScopePolicy:
return ScopePolicy(
scope_type=str(value.get("scope_type") or ""),
tenant_id=value.get("tenant_id"),
project_id=value.get("project_id"),
allowed_models=tuple(value.get("allowed_models") or ()),
denied_providers=tuple(value.get("denied_providers") or ()),
required_capabilities=tuple(value.get("required_capabilities") or ()),
pinned_model_id=value.get("pinned_model_id"),
pinned_model_version=value.get("pinned_model_version"),
max_input_tokens_per_request=int(value.get("max_input_tokens_per_request") or 0),
max_output_tokens_per_request=int(value.get("max_output_tokens_per_request") or 0),
daily_token_budget=int(value.get("daily_token_budget") or 0),
daily_cost_budget_micros=int(value.get("daily_cost_budget_micros") or 0),
fallback_limit=int(value.get("fallback_limit") or 0),
)
def _row_dict(row: object) -> dict[str, Any] | None:
return dict(row) if row is not None else None
def _provider_matches(row: Mapping[str, Any], desired: ProviderRecord) -> bool:
return (
row["credential_ref"] == desired.credential_ref
and bool(row["enabled"]) == desired.enabled
and row["failure_domain"] == desired.failure_domain
and int(row["max_failures"]) == desired.max_failures
and int(row["cooldown_seconds"]) == desired.cooldown_seconds
)
def _model_matches(row: Mapping[str, Any], desired: ModelRecord) -> bool:
return (
row["provider_id"] == desired.provider_id
and row["version"] == desired.version
and tuple(json.loads(row["capabilities_json"])) == tuple(sorted(set(desired.capabilities)))
and int(row["input_cost_micros_per_1k"]) == desired.input_cost_micros_per_1k
and int(row["output_cost_micros_per_1k"]) == desired.output_cost_micros_per_1k
and int(row["max_input_tokens"]) == desired.max_input_tokens
and int(row["max_output_tokens"]) == desired.max_output_tokens
and row["evaluation_state"] == desired.evaluation_state
and row["evaluation_sha256"] == desired.evaluation_sha256
and bool(row["enabled"]) == desired.enabled
and int(row["priority"]) == desired.priority
)
def _policy_matches(row: Mapping[str, Any], desired: ScopePolicy) -> bool:
return (
row["scope_type"] == desired.scope_type
and row["tenant_id"] == desired.tenant_id
and row["project_id"] == desired.project_id
and tuple(json.loads(row["allowed_models_json"])) == tuple(sorted(set(desired.allowed_models)))
and tuple(json.loads(row["denied_providers_json"])) == tuple(sorted(set(desired.denied_providers)))
and tuple(json.loads(row["required_capabilities_json"])) == tuple(sorted(set(desired.required_capabilities)))
and row["pinned_model_id"] == desired.pinned_model_id
and row["pinned_model_version"] == desired.pinned_model_version
and int(row["max_input_tokens_per_request"]) == desired.max_input_tokens_per_request
and int(row["max_output_tokens_per_request"]) == desired.max_output_tokens_per_request
and int(row["daily_token_budget"]) == desired.daily_token_budget
and int(row["daily_cost_budget_micros"]) == desired.daily_cost_budget_micros
and int(row["fallback_limit"]) == desired.fallback_limit
)
def apply_config(db_path: Path, config_path: Path) -> dict[str, Any]:
config = _load(config_path)
config_sha = hashlib.sha256(canonical(config)).hexdigest()
providers = config.get("providers") or []
models = config.get("models") or []
policies = config.get("policies") or []
if not all(isinstance(value, list) for value in (providers, models, policies)):
fail("MODEL_GOVERNANCE_CONFIG_COLLECTION_INVALID")
store = ModelGovernanceStore(db_path)
changes = {"providers": 0, "models": 0, "policies": 0, "unchanged": 0}
try:
for item in providers:
if not isinstance(item, Mapping):
fail("MODEL_GOVERNANCE_PROVIDER_CONFIG_INVALID")
desired = _provider_desired(item)
row = store.conn.execute(
"SELECT * FROM model_governance_providers WHERE provider_id=?",
(desired.provider_id,),
).fetchone()
if row is not None and _provider_matches(row, desired):
changes["unchanged"] += 1
continue
expected = int(row["revision"]) if row is not None else 0
store.register_provider(desired, expected_revision=expected)
changes["providers"] += 1
for item in models:
if not isinstance(item, Mapping):
fail("MODEL_GOVERNANCE_MODEL_CONFIG_INVALID")
desired = _model_desired(item)
row = store.conn.execute(
"SELECT * FROM model_governance_models WHERE model_id=?",
(desired.model_id,),
).fetchone()
if row is not None and _model_matches(row, desired):
changes["unchanged"] += 1
continue
expected = int(row["revision"]) if row is not None else 0
store.register_model(desired, expected_revision=expected)
changes["models"] += 1
for item in policies:
if not isinstance(item, Mapping):
fail("MODEL_GOVERNANCE_POLICY_CONFIG_INVALID")
desired = _policy_desired(item)
row = store.conn.execute(
"SELECT * FROM model_governance_scope_policies WHERE scope_key=?",
(desired.scope_key,),
).fetchone()
if row is not None and _policy_matches(row, desired):
changes["unchanged"] += 1
continue
expected = int(row["revision"]) if row is not None else 0
store.set_scope_policy(desired, expected_revision=expected)
changes["policies"] += 1
if not store.verify_audit_chain():
fail("MODEL_GOVERNANCE_AUDIT_CHAIN_INVALID")
return {
"schema": "aios.model-governance.bootstrap-result.v1",
"database": str(db_path),
"config_sha256": config_sha,
"changes": changes,
"audit_chain_valid": True,
}
finally:
store.close()
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--db", required=True)
parser.add_argument("--config", required=True)
parser.add_argument("--output")
args = parser.parse_args()
try:
result = apply_config(Path(args.db), Path(args.config))
except ModelGovernanceError as exc:
print(json.dumps({"status": "FAILED", "error": exc.code}, sort_keys=True))
return 20
data = json.dumps(result, indent=2, sort_keys=True) + "\n"
if args.output:
Path(args.output).write_text(data, encoding="utf-8")
print(data, end="")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -17,6 +17,7 @@ PHASE_ORDER = {
"claimed": 10,
"workspace_prepared": 20,
"verification_passed": 30,
"agent_runtime_done": 40,
"source_capture_done": 40,
"commit_done": 40,
"reconciliation_required": 45,
+209 -54
View File
@@ -236,6 +236,64 @@ def atomic_json(path: Path, value: Mapping[str, Any], mode: int = 0o640) -> None
os.unlink(temporary)
WORKER_LIVENESS_PATH = Path(
os.environ.get(
"AIOS_WORKER_LIVENESS_PATH",
str(STATE_ROOT / "worker-liveness.json"),
)
)
_LIVENESS_LOCK = threading.Lock()
_LIVENESS_SEQUENCE = 0
ENGINEERING_WRITE_CAPABILITIES = frozenset({
"governed.write.branch",
"governed.write.file",
})
def write_worker_liveness(
state: str,
*,
visible_count: int | None = None,
task_id: str | None = None,
stage: str | None = None,
error: str | None = None,
) -> None:
"""Write idle/busy liveness without touching Task/Job state."""
global _LIVENESS_SEQUENCE
with _LIVENESS_LOCK:
_LIVENESS_SEQUENCE += 1
epoch = time.time()
payload = {
"schema": "aios.online-agent.worker-liveness.v1",
"agent_id": AGENT_ID,
"agent_type": AGENT_TYPE,
"agent_version": AGENT_VERSION,
"pid": os.getpid(),
"state": state,
"sequence": _LIVENESS_SEQUENCE,
"last_seen_epoch": epoch,
"last_seen_at": datetime.fromtimestamp(epoch, timezone.utc).isoformat(),
"poll_seconds": POLL_SECONDS,
"heartbeat_seconds": HEARTBEAT_SECONDS,
"api_base": API_BASE,
"bridge_base": BRIDGE_BASE,
"visible_count": visible_count,
"task_id": task_id,
"stage": stage,
"error": error,
}
atomic_json(WORKER_LIVENESS_PATH, payload)
def source_capture_requires_deterministic_plan(envelope: "ExecutionEnvelope") -> bool:
"""Treat write-capable source_capture as a binding placeholder, never executable work."""
return (
envelope.action == "source_capture"
and bool(ENGINEERING_WRITE_CAPABILITIES.intersection(envelope.capability_scope))
)
def safe_relative_path(value: Any, *, field: str = "path") -> str:
if not isinstance(value, str) or not value or len(value) > 240:
raise WorkerError(f"{field.upper()}_INVALID", phase="validation")
@@ -525,11 +583,44 @@ class ExecutionEnvelope:
)
def external_agent_request_id(
envelope: ExecutionEnvelope,
runtime_name: str,
attempt: int,
) -> str:
if not isinstance(attempt, int) or isinstance(attempt, bool) or attempt < 1:
raise WorkerError("TASK_ATTEMPT_INVALID", phase="external_agent")
if runtime_name not in {"hermes", "openclaw"}:
raise WorkerError(
"EXTERNAL_AGENT_RUNTIME_UNSUPPORTED",
phase="external_agent",
details={"runtime": runtime_name},
)
return (
f"external-{runtime_name}-"
+ digest_json(
{
"task_id": envelope.task_id,
"job_id": envelope.job_id,
"execution_digest": envelope.digest,
"runtime": runtime_name,
"attempt": attempt,
}
)[:32]
)
class RuntimeAdapter(ABC):
name: str
@abstractmethod
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
raise NotImplementedError
@@ -550,7 +641,13 @@ class DeterministicExecutor(RuntimeAdapter):
if os.path.exists(temporary):
os.unlink(temporary)
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
changed: set[str] = set()
for change in envelope.change_set:
relative = str(change["path"])
@@ -605,7 +702,13 @@ class SourceCaptureExecutor(RuntimeAdapter):
"governed", "trace", "tool", "credential", "scope", "message", "handoff",
}
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
terms = set(self.defaults)
for signal in envelope.source_signals:
terms.update(token.lower() for token in re.findall(r"[A-Za-z][A-Za-z0-9_.-]{2,63}", signal))
@@ -674,7 +777,13 @@ class CompactInterfaceMapExecutor(RuntimeAdapter):
name = "compact_interface_map"
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
if envelope.action != "source_capture":
raise WorkerError("COMPACT_INTERFACE_MAP_ACTION_INVALID", phase="interface_map")
try:
@@ -704,7 +813,13 @@ class CompactInterfaceMapExecutor(RuntimeAdapter):
class CodexExecutor(RuntimeAdapter):
name = "codex"
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
raise WorkerError(
"CODEX_ADAPTER_REQUIRES_EXPLICIT_POLICY",
phase="adapter",
@@ -715,7 +830,13 @@ class CodexExecutor(RuntimeAdapter):
class ExternalAgentExecutor(RuntimeAdapter):
runtime_name: str
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
if envelope.action != "agent_runtime":
raise WorkerError(
"EXTERNAL_AGENT_ACTION_INVALID",
@@ -729,16 +850,10 @@ class ExternalAgentExecutor(RuntimeAdapter):
)
if not envelope.runtime_input:
raise WorkerError("EXTERNAL_AGENT_RUNTIME_INPUT_MISSING", phase="adapter")
request_id = (
f"external-{self.runtime_name}-"
+ digest_json(
{
"task_id": envelope.task_id,
"job_id": envelope.job_id,
"execution_digest": envelope.digest,
"runtime": self.runtime_name,
}
)[:32]
request_id = external_agent_request_id(
envelope,
self.runtime_name,
attempt,
)
exchange = ExternalAgentExchange(EXTERNAL_AGENT_EXECUTOR_ROOT)
try:
@@ -746,6 +861,7 @@ class ExternalAgentExecutor(RuntimeAdapter):
request_id=request_id,
runtime=self.runtime_name,
task_id=envelope.task_id,
attempt=attempt,
execution_digest=envelope.digest,
payload=envelope.runtime_input,
timeout_seconds=EXTERNAL_AGENT_EXECUTOR_TIMEOUT_SECONDS,
@@ -761,6 +877,7 @@ class ExternalAgentExecutor(RuntimeAdapter):
task_id=envelope.task_id,
adapter=self.runtime_name,
request_id=request_id,
attempt=attempt,
output_sha256=external.get("output_sha256"),
duration_ms=external.get("duration_ms"),
credential_material_exposed=False,
@@ -890,6 +1007,11 @@ class LeaseHeartbeat:
self.failed = str(response.get("error") or status)
raise WorkerError("TASK_HEARTBEAT_FAILED", phase="heartbeat", details={"api_status": status, "error": self.failed})
audit("TASK_HEARTBEAT", task_id=self.task_id, stage=self.stage, api_status=status)
write_worker_liveness(
"BUSY",
task_id=self.task_id,
stage=self.stage,
)
def start(self) -> None:
self.thread.start()
@@ -909,6 +1031,11 @@ class LeaseHeartbeat:
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, stage=stage, api_status=status, error=self.failed)
else:
audit("TASK_HEARTBEAT", task_id=self.task_id, stage=stage, api_status=status)
write_worker_liveness(
"BUSY",
task_id=self.task_id,
stage=stage,
)
except Exception as exc: # pragma: no cover - defensive runtime guard
self.failed = type(exc).__name__
audit("TASK_HEARTBEAT_FAILED", task_id=self.task_id, stage=stage, error=self.failed)
@@ -1286,7 +1413,13 @@ class IsolatedDeterministicExecutor(RuntimeAdapter):
if os.path.exists(temporary):
os.unlink(temporary)
def execute(self, envelope: ExecutionEnvelope, worktree: Path) -> dict[str, Any]:
def execute(
self,
envelope: ExecutionEnvelope,
worktree: Path,
*,
attempt: int,
) -> dict[str, Any]:
if envelope.action != "deterministic":
raise WorkerError("ISOLATED_EXECUTOR_ACTION_INVALID", phase="adapter")
root = ISOLATED_EXECUTOR_ROOT
@@ -1772,12 +1905,27 @@ class WorkerSupervisor:
if not isinstance(execution, Mapping):
raise WorkerError("EXECUTION_ENVELOPE_SCHEMA_VALIDATION", phase="validation")
persisted = ExecutionEnvelope.from_task(task, worker_capabilities=WORKER_CAPABILITIES)
# Deterministic tasks must re-resolve through the plan registry. A
# read-only source-capture task has no plan to replace its payload: replay
# the already validated durable envelope so source signals remain part of
# the canonical digest and the worker does not invent a different capture.
projection_input = execution if persisted.action == "source_capture" else None
# Read-only source_capture/agent_runtime tasks replay their persisted
# payload. A write-capable source_capture is only a Bridge-created
# 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
else None
)
status, authoritative = self.bridge.project_execution(task, projection_input)
if pending_plan and (
status == 404
or authoritative.get("error") == "TASK_EXECUTION_PROJECTION_MISSING"
):
raise WorkerError(
"TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN",
phase="projection",
)
if not 200 <= status < 300:
raise WorkerError("TASK_PERSISTED_BINDING_UNVERIFIED", phase="projection", details={"api_status": status, "error": authoritative.get("error")})
canonical = ExecutionEnvelope.from_task(task, authoritative, WORKER_CAPABILITIES)
@@ -1806,26 +1954,13 @@ class WorkerSupervisor:
execution_digest=canonical.digest,
)
return canonical, "plan_registry_verified"
if pending_plan:
raise WorkerError(
"TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN",
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"
raise WorkerError("TASK_SKIPPED_PROJECTION_MISSING", phase="projection")
def process_claim(
self,
@@ -2107,7 +2242,11 @@ class WorkerSupervisor:
save_task_state(task_id, state)
save_checkpoint(task_id, state, envelope, attempt)
adapter = adapter_for(envelope.adapter)
execution = adapter.execute(envelope, worktree)
execution = adapter.execute(
envelope,
worktree,
attempt=attempt,
)
state.update(
{
"phase": "verification_passed",
@@ -2290,17 +2429,24 @@ class WorkerSupervisor:
def once(self) -> int:
try:
tasks = self.agent.poll()
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)
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"}:
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"
@@ -2308,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:
@@ -2326,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
@@ -2334,6 +2488,7 @@ class WorkerSupervisor:
ensure_roots()
recover_stale_workspaces()
checkpoint_store()
write_worker_liveness("STARTING")
audit("WORKER_STARTED", task_agnostic=True, api_base=API_BASE, bridge_base=BRIDGE_BASE, adapter_names=["isolated-deterministic", "source_capture", "codex", "hermes"], runtime_only_credentials=True, isolated_executor=True, durable_checkpoint=True)
while True:
self.once()
@@ -0,0 +1,82 @@
"""Immutable release-lineage contracts used by activation and recovery."""
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import Any, Mapping
@dataclass(frozen=True)
class ReleaseLineage:
task_id: str
candidate_id: str
source_commit: str
version: str
required_capabilities: tuple[str, ...]
def as_dict(self) -> dict[str, Any]:
return asdict(self)
FIXED_V2R2C2 = ReleaseLineage(
task_id="r42-release-candidate-v2r2c2-live-20260819",
candidate_id="r42-release-candidate-v2r2c2-20260819",
source_commit="71e736b05fab13efc06af7a4fe2dc022c1cf9a85",
version="V2R2C2",
required_capabilities=("release:candidate",),
)
class ReleaseLineageError(RuntimeError):
pass
def validate_fixed_v2r2c2_task(task: Mapping[str, Any]) -> dict[str, Any]:
"""Validate identity/source/version without mutating Task state.
This contract intentionally does not bind status/attempt/revision, because
those are lifecycle state and may change through governed retry/recovery.
"""
if str(task.get("task_id") or "") != FIXED_V2R2C2.task_id:
raise ReleaseLineageError("FIXED_TASK_ID_DRIFT")
capabilities = task.get("required_capabilities")
if (
not isinstance(capabilities, list)
or tuple(capabilities) != FIXED_V2R2C2.required_capabilities
):
raise ReleaseLineageError("FIXED_TASK_CAPABILITY_DRIFT")
constraints = task.get("constraints")
if not isinstance(constraints, Mapping):
raise ReleaseLineageError("FIXED_TASK_CONSTRAINTS_MISSING")
release = constraints.get("release_candidate")
if not isinstance(release, Mapping):
raise ReleaseLineageError("FIXED_RELEASE_BINDING_MISSING")
actual = {
"candidate_id": str(release.get("candidate_id") or ""),
"source_commit": str(release.get("source_commit") or ""),
"version": str(release.get("version") or ""),
}
expected = {
"candidate_id": FIXED_V2R2C2.candidate_id,
"source_commit": FIXED_V2R2C2.source_commit,
"version": FIXED_V2R2C2.version,
}
if actual != expected:
raise ReleaseLineageError(
"FIXED_RELEASE_LINEAGE_DRIFT:"
+ repr({"expected": expected, "actual": actual})
)
return {
"schema": "aios.release-lineage.validation.v1",
"status": "VERIFIED",
"lineage": FIXED_V2R2C2.as_dict(),
"task_status": task.get("status"),
"attempt": task.get("attempt"),
"max_attempts": task.get("max_attempts"),
"revision": task.get("revision"),
}
@@ -0,0 +1,714 @@
#!/usr/bin/env python3
"""Persistent release-preparation Online Agent worker.
The worker is intentionally narrower than the generic engineering worker:
it can only consume tasks whose required capability set is exactly
{"release:candidate"} and whose payload asks for immutable release-candidate
creation. It never receives Owner signing, deployment, tag, baseline or Git
write credentials.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timezone
import hashlib
import json
import os
from pathlib import Path
import re
import socket
import threading
import time
import urllib.error
import urllib.request
from typing import Any, Mapping
from aios_autonomous_control_plane import (
classify_failure,
repair_for_failure,
runtime_admission,
)
AGENT_ID = os.environ.get(
"AIOS_RELEASE_WORKER_AGENT_ID",
"online-release-worker-cn-network-mkt-20260819",
).strip()
AGENT_VERSION = os.environ.get(
"AIOS_RELEASE_WORKER_AGENT_VERSION",
"2026-08-19.1-release-candidate",
).strip()
TASK_API = os.environ.get(
"AIOS_AGENT_TASK_API_URL",
"http://127.0.0.1:18940",
).rstrip("/")
RELEASE_API = os.environ.get("AIOS_RELEASE_API_URL", "").rstrip("/")
POLL_SECONDS = int(os.environ.get("AIOS_RELEASE_WORKER_POLL_SECONDS", "3"))
LEASE_SECONDS = int(os.environ.get("AIOS_RELEASE_WORKER_LEASE_SECONDS", "60"))
HEARTBEAT_SECONDS = int(
os.environ.get("AIOS_RELEASE_WORKER_HEARTBEAT_SECONDS", "15")
)
STATE_ROOT = Path(
os.environ.get(
"AIOS_RELEASE_WORKER_STATE_ROOT",
"/var/lib/aios-online-agent-release",
)
)
EXPECTED_CAPABILITIES = frozenset({"release:candidate"})
LIVENESS_PATH = Path(
os.environ.get(
"AIOS_WORKER_LIVENESS_PATH",
str(STATE_ROOT / "worker-liveness.json"),
)
)
CANDIDATE_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{7,255}$")
OID_RE = re.compile(r"^[0-9a-f]{40}$")
VERSION_RE = re.compile(r"^V[0-9]+R[0-9]+C[0-9]+$")
TASK_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{2,179}$")
SECRET_MARKERS = (
"authorization",
"token",
"secret",
"credential",
"password",
"private_key",
"signature",
)
class ReleaseWorkerError(RuntimeError):
def __init__(self, code: str, *, phase: str) -> None:
super().__init__(code)
self.code = code
self.phase = phase
def now() -> str:
return datetime.now(timezone.utc).isoformat()
def canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
def sha256(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def sanitized(value: Any) -> Any:
if isinstance(value, Mapping):
result: dict[str, Any] = {}
for key, inner in value.items():
lowered = str(key).lower()
if any(marker in lowered for marker in SECRET_MARKERS):
result[str(key)] = "[REDACTED]"
else:
result[str(key)] = sanitized(inner)
return result
if isinstance(value, list):
return [sanitized(item) for item in value]
return value
def audit(event: str, **fields: Any) -> None:
STATE_ROOT.mkdir(parents=True, exist_ok=True, mode=0o750)
path = STATE_ROOT / "events.jsonl"
record = {
"schema": "aios.release-worker.event.v1",
"event": event,
"created_at": now(),
"agent_id": AGENT_ID,
**sanitized(fields),
}
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(record, sort_keys=True) + "\n")
os.chmod(path, 0o640)
def sd_notify(message: str) -> None:
target = os.environ.get("NOTIFY_SOCKET", "")
if not target:
return
if target.startswith("@"):
address = "\0" + target[1:]
else:
address = target
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
sock.connect(address)
sock.sendall(message.encode("utf-8"))
except OSError:
# Liveness JSON remains the diagnostic source; systemd will enforce
# WatchdogSec if notify transport is persistently broken.
pass
finally:
sock.close()
def write_liveness(
state: str,
*,
task_id: str | None = None,
stage: str | None = None,
error: str | None = None,
visible_count: int | None = None,
) -> None:
STATE_ROOT.mkdir(parents=True, exist_ok=True, mode=0o750)
value = {
"schema": "aios.online-agent.worker-liveness.v1",
"agent_id": AGENT_ID,
"agent_type": "release-preparation-worker",
"agent_version": AGENT_VERSION,
"pid": os.getpid(),
"state": state,
"last_seen_epoch": time.time(),
"last_seen_at": now(),
"poll_seconds": POLL_SECONDS,
"heartbeat_seconds": HEARTBEAT_SECONDS,
"api_base": TASK_API,
"capabilities": sorted(EXPECTED_CAPABILITIES),
"task_id": task_id,
"stage": stage,
"error": error,
"visible_count": visible_count,
}
LIVENESS_PATH.parent.mkdir(parents=True, exist_ok=True, mode=0o750)
temporary = LIVENESS_PATH.with_name(
"." + LIVENESS_PATH.name + f".{os.getpid()}.tmp"
)
temporary.write_text(
json.dumps(value, ensure_ascii=False, sort_keys=True) + "\n",
encoding="utf-8",
)
os.chmod(temporary, 0o640)
os.replace(temporary, LIVENESS_PATH)
def load_credential(name: str) -> str:
directory = Path(os.environ.get("CREDENTIALS_DIRECTORY", ""))
path = directory / name
if path.is_symlink() or not path.is_file():
raise ReleaseWorkerError(
"CREDENTIAL_UNAVAILABLE:" + name,
phase="startup",
)
value = path.read_text(encoding="utf-8").strip()
if len(value) < 24 or any(ch.isspace() for ch in value):
raise ReleaseWorkerError(
"CREDENTIAL_INVALID:" + name,
phase="startup",
)
return value
def http_json(
base: str,
method: str,
route: str,
token: str,
body: Mapping[str, Any] | None = None,
*,
timeout: int = 30,
) -> tuple[int, dict[str, Any]]:
data = None if body is None else canonical(body)
headers = {"Accept": "application/json"}
if token:
headers["Authorization"] = "Bearer " + token
if data is not None:
headers["Content-Type"] = "application/json"
request = urllib.request.Request(
base + route,
method=method,
data=data,
headers=headers,
)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
raw, status = response.read(), response.status
except urllib.error.HTTPError as exc:
raw, status = exc.read(), exc.code
except (urllib.error.URLError, TimeoutError) as exc:
raise ReleaseWorkerError(
"HTTP_TRANSPORT_UNAVAILABLE",
phase="network",
) from exc
try:
value = json.loads(raw.decode())
except Exception as exc:
raise ReleaseWorkerError(
"HTTP_RESPONSE_INVALID",
phase="network",
) from exc
return status, value if isinstance(value, dict) else {}
@dataclass(frozen=True)
class ReleaseRequest:
candidate_id: str
source_commit: str
version: str
projection_id: str | None
@classmethod
def from_task(cls, task: Mapping[str, Any]) -> "ReleaseRequest":
if not TASK_RE.fullmatch(str(task.get("task_id") or "")):
raise ReleaseWorkerError("TASK_ID_INVALID", phase="validation")
caps = task.get("required_capabilities")
if (
not isinstance(caps, list)
or frozenset(caps) != EXPECTED_CAPABILITIES
or len(caps) != 1
):
raise ReleaseWorkerError(
"TASK_CAPABILITY_SCOPE_INVALID",
phase="validation",
)
metadata = task.get("metadata")
if not isinstance(metadata, Mapping):
metadata = {}
constraints = task.get("constraints")
if not isinstance(constraints, Mapping):
constraints = {}
release = constraints.get("release_candidate")
if not isinstance(release, Mapping):
release = metadata.get("release_candidate")
if not isinstance(release, Mapping):
release = task.get("release_candidate")
if not isinstance(release, Mapping):
raise ReleaseWorkerError(
"RELEASE_CANDIDATE_PAYLOAD_MISSING",
phase="validation",
)
allowed = {
"candidate_id",
"source_commit",
"version",
"projection_id",
}
if set(release) - allowed:
raise ReleaseWorkerError(
"RELEASE_CANDIDATE_PAYLOAD_FIELDS_INVALID",
phase="validation",
)
candidate_id = str(release.get("candidate_id") or "")
source_commit = str(release.get("source_commit") or "")
version = str(release.get("version") or "")
projection_id = release.get("projection_id")
if CANDIDATE_RE.fullmatch(candidate_id) is None:
raise ReleaseWorkerError(
"RELEASE_CANDIDATE_ID_INVALID",
phase="validation",
)
if OID_RE.fullmatch(source_commit) is None:
raise ReleaseWorkerError(
"RELEASE_SOURCE_COMMIT_INVALID",
phase="validation",
)
if VERSION_RE.fullmatch(version) is None:
raise ReleaseWorkerError(
"RELEASE_VERSION_INVALID",
phase="validation",
)
if projection_id is not None:
projection_id = str(projection_id)
if not re.fullmatch(r"projection-[0-9a-f]{32}", projection_id):
raise ReleaseWorkerError(
"RELEASE_PROJECTION_ID_INVALID",
phase="validation",
)
return cls(
candidate_id=candidate_id,
source_commit=source_commit,
version=version,
projection_id=projection_id,
)
def body(self) -> dict[str, str]:
return {
"candidate_id": self.candidate_id,
"source_commit": self.source_commit,
"version": self.version,
}
class Heartbeat:
def __init__(
self,
agent_token: str,
task_id: str,
lease_token: str,
) -> None:
self.agent_token = agent_token
self.task_id = task_id
self.lease_token = lease_token
self.stage = "claimed"
self.failure: str | None = None
self.stop = threading.Event()
self.thread = threading.Thread(
target=self._run,
name="release-worker-heartbeat",
daemon=True,
)
def first(self) -> None:
status, value = http_json(
TASK_API,
"POST",
f"/v1/tasks/{self.task_id}/heartbeat",
self.agent_token,
{
"lease_token": self.lease_token,
"lease_seconds": LEASE_SECONDS,
"progress": {
"stage": self.stage,
"worker_version": AGENT_VERSION,
},
},
)
if status != 200:
raise ReleaseWorkerError(
"TASK_HEARTBEAT_FAILED",
phase="heartbeat",
)
audit(
"TASK_HEARTBEAT",
task_id=self.task_id,
stage=self.stage,
api_status=status,
)
write_liveness(
"BUSY",
task_id=self.task_id,
stage=self.stage,
)
sd_notify("WATCHDOG=1")
def _run(self) -> None:
while not self.stop.wait(HEARTBEAT_SECONDS):
try:
status, value = http_json(
TASK_API,
"POST",
f"/v1/tasks/{self.task_id}/heartbeat",
self.agent_token,
{
"lease_token": self.lease_token,
"lease_seconds": LEASE_SECONDS,
"progress": {
"stage": self.stage,
"worker_version": AGENT_VERSION,
},
},
)
if status != 200:
self.failure = str(value.get("error") or status)
return
audit(
"TASK_HEARTBEAT",
task_id=self.task_id,
stage=self.stage,
api_status=status,
)
write_liveness(
"BUSY",
task_id=self.task_id,
stage=self.stage,
)
sd_notify("WATCHDOG=1")
except Exception as exc:
self.failure = type(exc).__name__
return
def ensure(self) -> None:
if self.failure:
raise ReleaseWorkerError(
"TASK_LEASE_LOST",
phase="heartbeat",
)
def close(self) -> None:
self.stop.set()
if self.thread.ident is not None:
self.thread.join(timeout=2)
def release_admission(
task: Mapping[str, Any],
*,
release_token: str,
):
required = task.get("required_capabilities")
if not isinstance(required, list):
required = []
def release_api_health() -> bool:
status, value = http_json(
RELEASE_API,
"GET",
"/health",
"",
timeout=10,
)
return status == 200 and str(value.get("status") or "").lower() in {"ok", "healthy"}
def authenticated_probe() -> bool:
status, _ = http_json(
RELEASE_API,
"POST",
"/createReleaseCandidate",
release_token,
{},
timeout=10,
)
# Authentication + credential projection succeeded; the empty release
# request is intentionally rejected by request validation.
return status == 400
probes = {
"credential_projection": authenticated_probe,
"trusted_builder": release_api_health,
"authenticated_probe": authenticated_probe,
"release_api_health": release_api_health,
"worker_liveness": lambda: True,
}
return runtime_admission(
worker_capabilities=EXPECTED_CAPABILITIES,
required_capabilities=required,
requested_operations=("prepare_release",),
probes=probes,
)
def result_payload(
task_id: str,
release: ReleaseRequest,
builder: Mapping[str, Any],
) -> dict[str, Any]:
builder_candidate = builder.get("candidate_id")
if builder_candidate not in {None, release.candidate_id}:
raise ReleaseWorkerError(
"BUILDER_CANDIDATE_BINDING_INVALID",
phase="release",
)
immutable = {
"candidate_id": release.candidate_id,
"source_commit": release.source_commit,
"version": release.version,
}
return {
"status": "SUCCEEDED",
"classification": "RELEASE_CANDIDATE_CREATED",
"execution_mode": "release_candidate",
"adapter": "release_candidate",
"task_id": task_id,
"agent_id": AGENT_ID,
"release_candidate": immutable,
"release_candidate_request_sha256": sha256(canonical(immutable)),
"builder_response": sanitized(dict(builder)),
"projection_id": release.projection_id,
"writer_lease": "NONE",
"changed_files": [],
"direct_main_write": False,
"production_write": False,
"tag_write": False,
"baseline_write": False,
"runtime_switch": False,
"owner_approval_submitted": False,
"credential_material_exposed": False,
}
def execute_task(
task: Mapping[str, Any],
*,
agent_token: str,
release_token: str,
) -> bool:
task_id = str(task.get("task_id") or "")
release = ReleaseRequest.from_task(task)
admission = release_admission(
task,
release_token=release_token,
)
if not admission.admitted:
audit(
"TASK_ADMISSION_BLOCKED",
task_id=task_id,
admission=admission.as_dict(),
)
write_liveness(
"BLOCKED",
task_id=task_id,
stage="execution_ready",
error=admission.failure_class.value if admission.failure_class else "ADMISSION_FAILED",
)
return False
audit(
"TASK_ADMISSION_PASSED",
task_id=task_id,
admission=admission.as_dict(),
)
status, claimed = http_json(
TASK_API,
"POST",
f"/v1/tasks/{task_id}/claim",
agent_token,
{"lease_seconds": LEASE_SECONDS},
)
if status in {409, 423}:
return False
if status != 200:
raise ReleaseWorkerError(
"TASK_CLAIM_FAILED",
phase="claim",
)
lease_token = str(claimed.get("lease_token") or "")
if len(lease_token) < 16:
raise ReleaseWorkerError(
"TASK_LEASE_TOKEN_INVALID",
phase="claim",
)
attempt = int(claimed.get("attempt") or 0)
audit(
"TASK_CLAIMED",
task_id=task_id,
attempt=attempt,
)
heartbeat = Heartbeat(agent_token, task_id, lease_token)
heartbeat.first()
heartbeat.thread.start()
try:
heartbeat.stage = "release_candidate"
heartbeat.ensure()
status, builder = http_json(
RELEASE_API,
"POST",
"/createReleaseCandidate",
release_token,
release.body(),
timeout=120,
)
if status not in {200, 201, 202}:
raise ReleaseWorkerError(
"RELEASE_CANDIDATE_CREATE_FAILED:"
+ str(builder.get("error") or status),
phase="release",
)
heartbeat.ensure()
result = result_payload(task_id, release, builder)
idempotency_key = (
f"{task_id}-attempt-{attempt}-release-result-v1"
)
status, submitted = http_json(
TASK_API,
"POST",
f"/v1/tasks/{task_id}/result",
agent_token,
{
"lease_token": lease_token,
"idempotency_key": idempotency_key,
"result": result,
},
)
if status not in {200, 201, 202}:
raise ReleaseWorkerError(
"TASK_RESULT_SUBMIT_FAILED",
phase="result",
)
audit(
"RESULT_SUBMITTED",
task_id=task_id,
attempt=attempt,
release_candidate_id=release.candidate_id,
)
return True
finally:
heartbeat.close()
def main() -> int:
if not RELEASE_API:
raise SystemExit("FATAL:AIOS_RELEASE_API_URL_REQUIRED")
agent_token = load_credential("agent_token")
release_token = load_credential("release_candidate_token")
STATE_ROOT.mkdir(parents=True, exist_ok=True, mode=0o750)
audit(
"WORKER_STARTED",
capabilities=sorted(EXPECTED_CAPABILITIES),
release_api=RELEASE_API,
)
write_liveness("STARTING", stage="startup")
sd_notify("READY=1\nSTATUS=release worker ready")
while True:
try:
status, response = http_json(
TASK_API,
"POST",
"/v1/tasks/poll",
agent_token,
{"limit": 100},
)
if status != 200:
raise ReleaseWorkerError(
"TASK_POLL_FAILED",
phase="poll",
)
tasks = response.get("tasks")
if not isinstance(tasks, list):
tasks = []
write_liveness(
"IDLE",
stage="poll",
visible_count=len(tasks),
)
sd_notify("WATCHDOG=1\nSTATUS=release worker poll ok")
handled = False
for raw in tasks:
if not isinstance(raw, Mapping):
continue
caps = raw.get("required_capabilities")
if (
not isinstance(caps, list)
or frozenset(caps) != EXPECTED_CAPABILITIES
):
continue
try:
handled = execute_task(
raw,
agent_token=agent_token,
release_token=release_token,
) or handled
except ReleaseWorkerError as exc:
failure = classify_failure(exc.code, phase=exc.phase)
repair = repair_for_failure(failure)
audit(
"TASK_EXECUTION_FAILED",
task_id=raw.get("task_id"),
phase=exc.phase,
error=exc.code,
failure_class=failure.value,
repair=repair.as_dict(),
)
time.sleep(0 if handled else POLL_SECONDS)
except KeyboardInterrupt:
return 0
except Exception as exc:
audit(
"WORKER_LOOP_ERROR",
error=type(exc).__name__,
)
write_liveness(
"DEGRADED",
stage="loop",
error=type(exc).__name__,
)
time.sleep(POLL_SECONDS)
if __name__ == "__main__":
raise SystemExit(main())
@@ -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,462 @@
"""Self-healing availability guard for persistent Online Agent workers.
The guard never claims Tasks and never writes Task/Job/Git databases. It only:
- ensures required worker/executor systemd units are enabled + active;
- checks worker-owned liveness JSON written by the real poll/heartbeat loop;
- restarts an active worker only when its liveness is stale;
- honors an explicit maintenance inhibit file;
- writes a bounded status JSON under /run for observability.
Idle liveness is the successful poll loop itself. Busy liveness is refreshed by the
Task lease-heartbeat thread, so long-running Tasks are not mistaken for hung workers.
"""
from __future__ import annotations
from dataclasses import asdict, dataclass
import argparse
import json
import os
from pathlib import Path
import shlex
import subprocess
import tempfile
import time
from typing import Any, Callable, Mapping, Sequence
SCHEMA = "aios.worker-availability.status.v1"
DEFAULT_WORKER_UNITS = (
"aios-online-agent-worker-20260815.service",
"aios-hermes-online-agent-worker.service",
"aios-openclaw-online-agent-worker.service",
)
DEFAULT_DEPENDENCY_UNITS = (
"aios-external-agent-executor.service",
)
DEFAULT_RUN_ROOT = Path("/run/aios-worker-availability")
DEFAULT_STATUS = DEFAULT_RUN_ROOT / "status.json"
DEFAULT_RECOVERY = DEFAULT_RUN_ROOT / "recovery.json"
DEFAULT_INHIBIT = DEFAULT_RUN_ROOT / "maintenance.lock"
DEFAULT_MAX_STALE_SECONDS = 90
DEFAULT_RESTART_COOLDOWN_SECONDS = 60
DEFAULT_STARTUP_WAIT_SECONDS = 10
ACCEPTED_ENABLED_STATES = frozenset({"enabled", "enabled-runtime", "static", "indirect", "generated"})
class AvailabilityGuardError(RuntimeError):
pass
@dataclass(frozen=True)
class UnitSnapshot:
unit: str
role: str
load_state: str
active_state: str
unit_file_state: str
enabled_ok: bool
liveness_path: str | None = None
liveness_state: str | None = None
last_seen_epoch: float | None = None
liveness_age_seconds: float | None = None
fresh: bool | None = None
action: str = "NONE"
detail: str | None = None
class Systemd:
def __init__(
self,
runner: Callable[..., subprocess.CompletedProcess[str]] | None = None,
) -> None:
self.runner = runner or subprocess.run
def _run(self, args: Sequence[str], *, check: bool = False) -> subprocess.CompletedProcess[str]:
result = self.runner(
["/usr/bin/systemctl", *args],
text=True,
capture_output=True,
check=False,
timeout=20,
)
if check and result.returncode != 0:
raise AvailabilityGuardError(
"SYSTEMD_COMMAND_FAILED:"
+ ":".join(args)
+ ":"
+ str(result.returncode)
)
return result
def property(self, unit: str, name: str) -> str:
result = self._run(["show", unit, f"--property={name}", "--value"])
if result.returncode != 0:
return ""
return result.stdout.strip()
def environment(self, unit: str) -> dict[str, str]:
text = self.property(unit, "Environment")
result: dict[str, str] = {}
for item in shlex.split(text):
if "=" not in item:
continue
key, value = item.split("=", 1)
result[key] = value
return result
def enable(self, unit: str) -> None:
self._run(["enable", unit], check=True)
def start(self, unit: str) -> None:
self._run(["start", unit], check=True)
def restart(self, unit: str) -> None:
self._run(["restart", unit], check=True)
def _atomic_json(path: Path, value: Mapping[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True, mode=0o755)
if path.parent.is_symlink() or path.is_symlink():
raise AvailabilityGuardError("STATUS_PATH_SYMLINK_REJECTED")
fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
try:
os.fchmod(fd, 0o644)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(value, handle, ensure_ascii=False, sort_keys=True, indent=2)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
os.chmod(path, 0o644)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
def _load_json(path: Path) -> dict[str, Any] | None:
if path.is_symlink() or not path.is_file():
return None
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return None
return dict(value) if isinstance(value, Mapping) else None
def _state_root(environment: Mapping[str, str], unit: str) -> Path:
explicit = environment.get("AIOS_WORKER_STATE_ROOT")
if explicit:
return Path(explicit)
fallbacks = {
"aios-online-agent-worker-20260815.service": Path(
"/var/lib/aios-online-agent-worker-20260815"
),
"aios-hermes-online-agent-worker.service": Path(
"/var/lib/aios-online-agent-hermes"
),
"aios-openclaw-online-agent-worker.service": Path(
"/var/lib/aios-online-agent-openclaw"
),
}
if unit not in fallbacks:
raise AvailabilityGuardError("WORKER_STATE_ROOT_UNAVAILABLE:" + unit)
return fallbacks[unit]
def _liveness_path(environment: Mapping[str, str], unit: str) -> Path:
explicit = environment.get("AIOS_WORKER_LIVENESS_PATH")
if explicit:
return Path(explicit)
return _state_root(environment, unit) / "worker-liveness.json"
class AvailabilityGuard:
def __init__(
self,
*,
systemd: Systemd | None = None,
worker_units: Sequence[str] = DEFAULT_WORKER_UNITS,
dependency_units: Sequence[str] = DEFAULT_DEPENDENCY_UNITS,
status_path: Path = DEFAULT_STATUS,
recovery_path: Path = DEFAULT_RECOVERY,
inhibit_path: Path = DEFAULT_INHIBIT,
max_stale_seconds: int = DEFAULT_MAX_STALE_SECONDS,
restart_cooldown_seconds: int = DEFAULT_RESTART_COOLDOWN_SECONDS,
startup_wait_seconds: int = DEFAULT_STARTUP_WAIT_SECONDS,
now_fn: Callable[[], float] = time.time,
sleep_fn: Callable[[float], None] = time.sleep,
) -> None:
self.systemd = systemd or Systemd()
self.worker_units = tuple(worker_units)
self.dependency_units = tuple(dependency_units)
self.status_path = Path(status_path)
self.recovery_path = Path(recovery_path)
self.inhibit_path = Path(inhibit_path)
self.max_stale_seconds = int(max_stale_seconds)
self.restart_cooldown_seconds = int(restart_cooldown_seconds)
self.startup_wait_seconds = int(startup_wait_seconds)
self.now_fn = now_fn
self.sleep_fn = sleep_fn
if self.max_stale_seconds < 15:
raise AvailabilityGuardError("MAX_STALE_SECONDS_TOO_SMALL")
if self.restart_cooldown_seconds < 15:
raise AvailabilityGuardError("RESTART_COOLDOWN_TOO_SMALL")
if self.startup_wait_seconds < 0 or self.startup_wait_seconds > 60:
raise AvailabilityGuardError("STARTUP_WAIT_INVALID")
def _recovery_state(self) -> dict[str, float]:
value = _load_json(self.recovery_path) or {}
raw = value.get("last_restart_epoch")
if not isinstance(raw, Mapping):
return {}
result: dict[str, float] = {}
for unit, epoch in raw.items():
try:
result[str(unit)] = float(epoch)
except (TypeError, ValueError):
continue
return result
def _save_recovery_state(self, state: Mapping[str, float]) -> None:
_atomic_json(
self.recovery_path,
{
"schema": "aios.worker-availability.recovery.v1",
"last_restart_epoch": dict(state),
},
)
def _basic_snapshot(self, unit: str, role: str) -> tuple[str, str, str, bool]:
load = self.systemd.property(unit, "LoadState")
active = self.systemd.property(unit, "ActiveState")
enabled = self.systemd.property(unit, "UnitFileState")
return load, active, enabled, enabled in ACCEPTED_ENABLED_STATES
def _ensure_enabled_active(self, unit: str, role: str) -> tuple[str, bool]:
load, active, enabled, enabled_ok = self._basic_snapshot(unit, role)
if load != "loaded":
raise AvailabilityGuardError("REQUIRED_UNIT_NOT_LOADED:" + unit)
action = "NONE"
if not enabled_ok:
self.systemd.enable(unit)
action = "ENABLE"
if active != "active":
self.systemd.start(unit)
action = "START" if action == "NONE" else "ENABLE_START"
return action, active == "active"
def _wait_for_liveness(self, path: Path) -> dict[str, Any] | None:
deadline = self.now_fn() + self.startup_wait_seconds
while True:
value = _load_json(path)
if value is not None:
try:
epoch = float(value.get("last_seen_epoch"))
except (TypeError, ValueError):
epoch = 0.0
if epoch > 0 and self.now_fn() - epoch <= self.max_stale_seconds:
return value
if self.now_fn() >= deadline:
return value
self.sleep_fn(0.5)
def run(self, *, heal: bool) -> dict[str, Any]:
now = self.now_fn()
if self.inhibit_path.exists():
result = {
"schema": SCHEMA,
"status": "MAINTENANCE_INHIBITED",
"generated_at_epoch": now,
"maintenance_inhibit": str(self.inhibit_path),
"workers": [],
"dependencies": [],
"all_fresh": False,
"actions": [],
}
_atomic_json(self.status_path, result)
return result
actions: list[dict[str, str]] = []
dependencies: list[UnitSnapshot] = []
for unit in self.dependency_units:
load, active, enabled, enabled_ok = self._basic_snapshot(unit, "dependency")
action = "NONE"
if heal:
if load != "loaded":
raise AvailabilityGuardError("REQUIRED_UNIT_NOT_LOADED:" + unit)
if not enabled_ok:
self.systemd.enable(unit)
action = "ENABLE"
if active != "active":
self.systemd.start(unit)
action = "START" if action == "NONE" else "ENABLE_START"
if action != "NONE":
actions.append({"unit": unit, "action": action})
load, active, enabled, enabled_ok = self._basic_snapshot(unit, "dependency")
dependencies.append(
UnitSnapshot(
unit=unit,
role="dependency",
load_state=load,
active_state=active,
unit_file_state=enabled,
enabled_ok=enabled_ok,
fresh=(active == "active"),
action=action,
)
)
recovery = self._recovery_state()
workers: list[UnitSnapshot] = []
for unit in self.worker_units:
load, active, enabled, enabled_ok = self._basic_snapshot(unit, "worker")
environment = self.systemd.environment(unit) if load == "loaded" else {}
path = _liveness_path(environment, unit) if load == "loaded" else None
action = "NONE"
if heal:
if load != "loaded":
raise AvailabilityGuardError("REQUIRED_UNIT_NOT_LOADED:" + unit)
if not enabled_ok:
self.systemd.enable(unit)
action = "ENABLE"
if active != "active":
self.systemd.start(unit)
action = "START" if action == "NONE" else "ENABLE_START"
actions.append({"unit": unit, "action": action})
if path is not None:
self._wait_for_liveness(path)
load, active, enabled, enabled_ok = self._basic_snapshot(unit, "worker")
live = _load_json(path) if path is not None else None
last_seen: float | None = None
state: str | None = None
age: float | None = None
fresh = False
detail: str | None = None
if live is not None:
state = str(live.get("state") or "")
try:
last_seen = float(live.get("last_seen_epoch"))
except (TypeError, ValueError):
last_seen = None
if last_seen is not None:
age = max(0.0, now - last_seen)
fresh = age <= self.max_stale_seconds
if live.get("agent_id") in (None, ""):
fresh = False
detail = "LIVENESS_AGENT_ID_MISSING"
else:
detail = "LIVENESS_MISSING"
if heal and active == "active" and not fresh:
previous = float(recovery.get(unit, 0.0))
if now - previous >= self.restart_cooldown_seconds:
self.systemd.restart(unit)
recovery[unit] = now
action = "RESTART_STALE"
actions.append({"unit": unit, "action": action})
if path is not None:
live = self._wait_for_liveness(path)
load, active, enabled, enabled_ok = self._basic_snapshot(unit, "worker")
if live is not None:
state = str(live.get("state") or "")
try:
last_seen = float(live.get("last_seen_epoch"))
except (TypeError, ValueError):
last_seen = None
if last_seen is not None:
age = max(0.0, self.now_fn() - last_seen)
fresh = age <= self.max_stale_seconds
detail = None if fresh else "LIVENESS_STALE_AFTER_RESTART"
else:
detail = "RESTART_COOLDOWN_ACTIVE"
workers.append(
UnitSnapshot(
unit=unit,
role="worker",
load_state=load,
active_state=active,
unit_file_state=enabled,
enabled_ok=enabled_ok,
liveness_path=str(path) if path is not None else None,
liveness_state=state,
last_seen_epoch=last_seen,
liveness_age_seconds=age,
fresh=bool(active == "active" and enabled_ok and fresh),
action=action,
detail=detail,
)
)
if heal:
self._save_recovery_state(recovery)
all_fresh = (
all(item.active_state == "active" and item.enabled_ok and item.fresh for item in workers)
and all(item.active_state == "active" for item in dependencies)
)
result = {
"schema": SCHEMA,
"status": "HEALTHY" if all_fresh else "DEGRADED",
"generated_at_epoch": self.now_fn(),
"max_stale_seconds": self.max_stale_seconds,
"maintenance_inhibit": str(self.inhibit_path),
"workers": [asdict(item) for item in workers],
"dependencies": [asdict(item) for item in dependencies],
"all_fresh": all_fresh,
"actions": actions,
}
_atomic_json(self.status_path, result)
return result
def main() -> int:
parser = argparse.ArgumentParser()
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--heal", action="store_true")
mode.add_argument("--check", action="store_true")
parser.add_argument("--status-file", default=str(DEFAULT_STATUS))
parser.add_argument("--recovery-file", default=str(DEFAULT_RECOVERY))
parser.add_argument("--inhibit-file", default=str(DEFAULT_INHIBIT))
parser.add_argument(
"--max-stale-seconds",
type=int,
default=int(
os.environ.get(
"AIOS_WORKER_AVAILABILITY_MAX_STALE_SECONDS",
str(DEFAULT_MAX_STALE_SECONDS),
)
),
)
parser.add_argument(
"--restart-cooldown-seconds",
type=int,
default=int(
os.environ.get(
"AIOS_WORKER_AVAILABILITY_RESTART_COOLDOWN_SECONDS",
str(DEFAULT_RESTART_COOLDOWN_SECONDS),
)
),
)
args = parser.parse_args()
guard = AvailabilityGuard(
status_path=Path(args.status_file),
recovery_path=Path(args.recovery_file),
inhibit_path=Path(args.inhibit_file),
max_stale_seconds=args.max_stale_seconds,
restart_cooldown_seconds=args.restart_cooldown_seconds,
)
try:
result = guard.run(heal=args.heal)
except AvailabilityGuardError as exc:
print(json.dumps({"status": "FAILED", "error": str(exc)}, sort_keys=True))
return 20
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
if args.check and not result.get("all_fresh"):
return 10
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -6,7 +6,7 @@
"--provider",
"oneapi",
"-m",
"deepseek-v4-flash",
"{model_id}",
"--no-restore-cwd"
],
"credential_env": {
@@ -21,6 +21,16 @@
},
"input_mode": "stdin_text",
"max_output_bytes": 131072,
"model_governance": {
"max_output_tokens": 512,
"model_capabilities": [
"text"
],
"provider_ids": [
"oneapi"
],
"required": true
},
"output_mode": "stdout_text",
"runtime": "hermes",
"schema": "aios.external-agent-runtime.profile.v1",
@@ -12,7 +12,7 @@
"--message",
"{prompt}",
"--model",
"openai/deepseek-v4-flash",
"{model_id}",
"--timeout",
"180"
],
@@ -30,6 +30,17 @@
},
"input_mode": "stdin_text",
"max_output_bytes": 131072,
"model_governance": {
"input_reservation_floor_tokens": 16384,
"max_output_tokens": 256,
"model_capabilities": [
"text"
],
"provider_ids": [
"openai"
],
"required": true
},
"output_mode": "stdout_json",
"runtime": "openclaw",
"schema": "aios.external-agent-runtime.profile.v1",
@@ -0,0 +1,95 @@
{
"models": [
{
"capabilities": [
"agent.runtime.hermes",
"text"
],
"enabled": true,
"evaluation_sha256": "43ae0db1640daeb6d33e2787769732844f366d36070065d3a2239905bd9a823b",
"evaluation_state": "APPROVED",
"input_cost_micros_per_1k": 0,
"max_input_tokens": 32768,
"max_output_tokens": 512,
"model_id": "deepseek-v4-flash",
"output_cost_micros_per_1k": 0,
"priority": 10,
"provider_id": "oneapi",
"version": "profile-sha:54615e533bfdb6a2e8b870ae74bb8cd7fc45177495bcf6e8c45a50837cefb85a"
},
{
"capabilities": [
"agent.runtime.openclaw",
"text"
],
"enabled": true,
"evaluation_sha256": "7b1c89634eb9c0e7cb5a3d75323d904ebbd91b3ae5494ce938bd8316a4cf5b12",
"evaluation_state": "APPROVED",
"input_cost_micros_per_1k": 0,
"max_input_tokens": 32768,
"max_output_tokens": 256,
"model_id": "openai/deepseek-v4-flash",
"output_cost_micros_per_1k": 0,
"priority": 10,
"provider_id": "openai",
"version": "profile-sha:2a31c480226aa78d44ed9692bddcedc99c3aa03c23c0dd5f031a5cbbb8bbd7ba"
}
],
"policies": [
{
"allowed_models": [
"deepseek-v4-flash",
"openai/deepseek-v4-flash"
],
"daily_cost_budget_micros": 0,
"daily_token_budget": 250000,
"denied_providers": [],
"fallback_limit": 1,
"max_input_tokens_per_request": 16384,
"max_output_tokens_per_request": 512,
"project_id": null,
"required_capabilities": [
"text"
],
"scope_type": "legacy",
"tenant_id": null
},
{
"allowed_models": [
"deepseek-v4-flash",
"openai/deepseek-v4-flash"
],
"daily_cost_budget_micros": 0,
"daily_token_budget": 250000,
"denied_providers": [],
"fallback_limit": 1,
"max_input_tokens_per_request": 16384,
"max_output_tokens_per_request": 512,
"project_id": "staging",
"required_capabilities": [
"text"
],
"scope_type": "tenant",
"tenant_id": "model-governance-live"
}
],
"providers": [
{
"cooldown_seconds": 60,
"credential_ref": "runtime-profile:hermes",
"enabled": true,
"failure_domain": "oneapi",
"max_failures": 2,
"provider_id": "oneapi"
},
{
"cooldown_seconds": 60,
"credential_ref": "runtime-profile:openclaw",
"enabled": true,
"failure_domain": "oneapi",
"max_failures": 2,
"provider_id": "openai"
}
],
"schema": "aios.model-governance.bootstrap.v1"
}
@@ -0,0 +1,37 @@
[Unit]
Description=AIOS owner-controlled Task initial execution 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-initial-execution-projection-recovery-20260820
Environment=AIOS_TASK_INITIAL_EXECUTION_PROJECTION_HOST=127.0.0.1
Environment=AIOS_TASK_INITIAL_EXECUTION_PROJECTION_PORT=18943
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_agent_task_initial_execution_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,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
@@ -33,7 +33,10 @@ InaccessiblePaths=/data/agent/secrets
ReadOnlyPaths=/etc/aios/external-agent-runtime.d
ReadOnlyPaths=/opt/aios-hermes-agent
ReadOnlyPaths=/opt/aios/openclaw-engineering
Environment=AIOS_MODEL_GOVERNANCE_DB=/var/lib/aios-model-governance/state.sqlite3
Environment=AIOS_MODEL_GOVERNANCE_ENABLED=1
ReadWritePaths=/var/lib/aios-external-agent-executor
ReadWritePaths=/var/lib/aios-model-governance
UMask=0027
[Install]
@@ -0,0 +1,41 @@
[Unit]
Description=AIOS disabled-by-default HTTPS edge for GPT read-only Online Task ingress
After=network-online.target aios-gpt-online-task-ingress.service
Wants=network-online.target aios-gpt-online-task-ingress.service
ConditionPathExists=/etc/aios-gitops/gpt-https-edge.approved
[Service]
Type=simple
DynamicUser=yes
LoadCredential=tls_cert:/etc/aios-gitops/gpt-https-edge-tls.crt
LoadCredential=tls_key:/etc/aios-gitops/gpt-https-edge-tls.key
Environment=AIOS_GPT_EDGE_TLS_CERT_FILE=%d/tls_cert
Environment=AIOS_GPT_EDGE_TLS_KEY_FILE=%d/tls_key
Environment=AIOS_GPT_EDGE_HOST=127.0.0.1
Environment=AIOS_GPT_EDGE_PORT=18443
Environment=AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=0
Environment=AIOS_GPT_EDGE_PUBLIC_DOMAIN=
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_gpt_https_edge.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectClock=yes
LockPersonality=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_INET AF_INET6
InaccessiblePaths=/data/aios-gitops /etc/aios-gitops /home /var/lib/aios-agent-task
UMask=0077
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,45 @@
[Unit]
Description=AIOS external GPT read-only Online Task ingress
After=network.target aios-chatgpt-bridge-v2-staging.service aios-agent-task-api.service
Wants=aios-chatgpt-bridge-v2-staging.service aios-agent-task-api.service
[Service]
Type=simple
DynamicUser=yes
StateDirectory=aios-gpt-online-task-ingress
StateDirectoryMode=0750
LoadCredential=gpt_ingress_token:/etc/aios-gitops/gpt-online-task-ingress.token
LoadCredential=agent_task_control_token:/etc/aios-gitops/agent-task-control.token
Environment=AIOS_GPT_INGRESS_TOKEN_FILE=%d/gpt_ingress_token
Environment=AIOS_AGENT_TASK_CONTROL_TOKEN_FILE=%d/agent_task_control_token
Environment=AIOS_GPT_INGRESS_STATE_ROOT=%S/aios-gpt-online-task-ingress
Environment=AIOS_GPT_INGRESS_HOST=127.0.0.1
Environment=AIOS_GPT_INGRESS_PORT=18945
Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_gpt_online_task_ingress.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectClock=yes
LockPersonality=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_INET
IPAddressDeny=any
IPAddressAllow=127.0.0.0/8
InaccessiblePaths=/data/aios-gitops /etc/aios-gitops /home
UMask=0077
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,47 @@
[Unit]
Description=AIOS durable Owner authorization broker
After=network.target aios-chatgpt-bridge-v2-staging.service
Wants=aios-chatgpt-bridge-v2-staging.service
[Service]
Type=simple
User=root
Group=aios-executor-exchange
RuntimeDirectory=aios-owner-authorization-broker
RuntimeDirectoryMode=0750
StateDirectory=aios-owner-authorization-broker
StateDirectoryMode=0750
LoadCredential=agent_task_control_token:/etc/aios-gitops/agent-task-control.token
Environment=AIOS_AGENT_TASK_CONTROL_TOKEN_FILE=%d/agent_task_control_token
Environment=AIOS_OWNER_DURABLE_AUTHORIZATION_POLICY=/etc/aios-gitops/owner-durable-authorization.json
Environment=AIOS_OWNER_DURABLE_AUTHORIZATION_REVOCATIONS=/etc/aios-gitops/owner-durable-authorization-revocations.json
Environment=AIOS_OWNER_AUTH_BROKER_ACCOUNT=chatgpt-governed-implementer-singlehost
Environment=AIOS_OWNER_AUTH_BROKER_SOCKET=%t/aios-owner-authorization-broker/broker.sock
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_durable_owner_authorization.py
Restart=on-failure
RestartSec=2
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectClock=yes
LockPersonality=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_INET
IPAddressDeny=any
IPAddressAllow=127.0.0.0/8
ReadOnlyPaths=/etc/aios-gitops/owner-durable-authorization.json
ReadOnlyPaths=/etc/aios-gitops/owner-durable-authorization-revocations.json
InaccessiblePaths=/data/aios-gitops /home
UMask=0007
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,50 @@
[Unit]
Description=AIOS release-candidate Online Agent worker
After=network.target aios-agent-task-api.service
Wants=aios-agent-task-api.service
[Service]
Type=notify
NotifyAccess=main
WatchdogSec=90
User=root
Group=root
StateDirectory=aios-online-agent-release
StateDirectoryMode=0750
LoadCredentialEncrypted=agent_token:/etc/credstore.encrypted/aios-release-online-agent-worker/agent_token
LoadCredentialEncrypted=release_candidate_token:/etc/credstore.encrypted/aios-release-online-agent-worker/release_candidate_token
Environment=AIOS_RELEASE_WORKER_AGENT_ID=online-release-worker-cn-network-mkt-20260819
Environment=AIOS_RELEASE_WORKER_AGENT_VERSION=2026-08-19.1-release-candidate
Environment=AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940
Environment=AIOS_RELEASE_WORKER_STATE_ROOT=/var/lib/aios-online-agent-release
Environment=AIOS_WORKER_STATE_ROOT=/var/lib/aios-online-agent-release
Environment=AIOS_RELEASE_WORKER_POLL_SECONDS=3
Environment=AIOS_RELEASE_WORKER_LEASE_SECONDS=60
Environment=AIOS_RELEASE_WORKER_HEARTBEAT_SECONDS=15
Environment=AIOS_RELEASE_API_URL=http://127.0.0.1:18890
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_release_online_agent_worker.py
Restart=always
RestartSec=2
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectClock=yes
LockPersonality=yes
RestrictSUIDSGID=yes
RestrictRealtime=yes
RestrictNamespaces=yes
MemoryDenyWriteExecute=yes
RestrictAddressFamilies=AF_UNIX AF_INET
IPAddressDeny=any
IPAddressAllow=127.0.0.0/8
InaccessiblePaths=/data/aios-gitops /home
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
@@ -0,0 +1,27 @@
[Unit]
Description=AIOS persistent Online Agent worker availability guard
After=local-fs.target
[Service]
Type=oneshot
User=root
Group=root
Environment=PYTHONUNBUFFERED=1
Environment=AIOS_WORKER_AVAILABILITY_MAX_STALE_SECONDS=90
Environment=AIOS_WORKER_AVAILABILITY_RESTART_COOLDOWN_SECONDS=60
ExecStart=/usr/bin/python3 -B /opt/aios/current/active-runtime/app/aios_worker_availability_guard.py --heal
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
ReadWritePaths=/run/aios-worker-availability
UMask=0022
@@ -0,0 +1,13 @@
[Unit]
Description=Periodically ensure AIOS Online Agent workers remain available
[Timer]
OnBootSec=15s
OnUnitActiveSec=30s
AccuracySec=5s
RandomizedDelaySec=3s
Persistent=true
Unit=aios-worker-availability-guard.service
[Install]
WantedBy=timers.target
@@ -0,0 +1 @@
d /var/lib/aios-model-governance 0750 aios-engineering-executor aios-executor-exchange -
@@ -0,0 +1 @@
d /run/aios-worker-availability 0755 root root -
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,123 @@
# External Agent Attempt-Scoped Request Identity
## Problem
External Agent execution previously derived its durable request ID from:
```text
task_id
job_id
execution_digest
runtime
```
The Online Task attempt was missing.
That is incorrect for retryable external side effects. A failed Model Governance route reservation is
durable and terminal for that request ID. Reusing the same external request ID on a later official
Task attempt therefore reuses the old route decision/reservation and can fail settlement with:
```text
MODEL_RESERVATION_STATE_INVALID
```
Archiving an exchange failed marker does not fix the root cause because the Model Governance
reservation remains intentionally durable.
## Contract
External request identity is now:
```text
hash(
task_id,
job_id,
execution_digest,
runtime,
attempt
)
```
The attempt is a **transient execution coordinate**. It is not added to the signed governed execution
envelope and does not alter the Task/Job binding or execution digest.
The worker gets the authoritative attempt from the successful claim response and passes it through:
```text
process_claim
→ RuntimeAdapter.execute(..., attempt=N)
→ ExternalAgentExecutor
→ ExternalAgentExchange
→ external request JSON
→ external result JSON
```
The external result must bind the same attempt.
## Idempotency
This gives the intended two-level behavior:
```text
same Task/Job/execution/runtime + same attempt
→ same external request ID
→ same external result/reservation can be replayed safely
same Task/Job/execution/runtime + next official attempt
→ different external request ID
→ different Model Governance request ID
→ new reservation
```
Old FAILED reservations are retained for audit. No Model Governance DB cleanup is required or
permitted.
## Model Governance
Model Governance already derives its route request ID from:
```text
external_request_id + ":model"
```
Therefore making the external request ID attempt-scoped automatically makes route decisions and
reservations attempt-scoped without creating another state authority.
## Recovery
For the current Hermes live Task:
```text
attempt 6
→ old request/reservation remains FAILED
official NEEDS_REWORK
→ bounded retry extension 6 → 7
attempt 7
→ new external request ID
→ new route request ID
→ new reservation
→ provider execution
→ settlement
```
No replacement Task/Job, direct DB rewrite, failed-reservation deletion or marker deletion is
required.
## Worker Availability
The repair must preserve the already verified Worker Availability runtime. Runtime switches use the
maintenance inhibit and must finish with the guard healthy and all three workers fresh.
## Verification
Required regression coverage:
- request ID stable within one attempt;
- request ID different across attempts;
- request schema requires a valid attempt;
- external result binds attempt;
- old failed Model Governance reservation remains FAILED;
- next attempt gets a new reservation and can settle;
- existing external-agent and Model Governance runtime tests still pass.
@@ -0,0 +1,109 @@
# Model Governance Policy / Profile Floor Compatibility
## Problem
The root-owned OpenClaw runtime profile reserves a minimum of 16,384 input tokens so Model
Governance can cover OpenClaw-injected workspace, skills and system context before provider
execution.
The staging scope policies still limited input to 16,000 tokens per request. Route selection
therefore failed before reservation with:
```text
MODEL_INPUT_TOKEN_LIMIT_DENY
```
This was a configuration invariant violation:
```text
scope max_input_tokens_per_request
<
root-owned profile input_reservation_floor_tokens
```
The registered OpenClaw model itself supports 32,768 input tokens, so the model capability was not
the blocker.
## Invariant
For every Model Governance scope that allows a model/runtime whose root-owned profile declares an
input reservation floor:
```text
scope.max_input_tokens_per_request
>=
profile.model_governance.input_reservation_floor_tokens
```
The profile floor remains root-owned. Task payloads cannot alter it.
## Staging policy
The two staging policies that allow `openai/deepseek-v4-flash` are updated from:
```text
16000
```
to:
```text
16384
```
This is the minimum compatible value. It intentionally remains below the model's 32,768-token
technical input ceiling.
The daily token budget is unchanged.
## Governance path
The live SQLite policy must be changed only through the repository bootstrap and
`aios_model_governance_admin.py`.
The admin reconciler:
- reads the governed repository config;
- uses current policy revision as CAS;
- calls `ModelGovernanceStore.set_scope_policy`;
- appends normal Model Governance audit evidence;
- verifies the audit chain.
Direct SQL updates remain forbidden.
## Exhausted OpenClaw recovery
The fixed OpenClaw live Task has exhausted its normal three attempts.
After the governed policy candidate is officially verified and the live policy is reconciled:
```text
attempt 3 VERIFYING/FAILED result
→ official NEEDS_REWORK
→ Task becomes FAILED 3/3
→ official bounded retry extension 3 → 4
→ same Task / same Job returns QUEUED
→ persistent OpenClaw worker auto-claims attempt 4
```
AR1 attempt-scoped external request identity ensures attempt 4 receives a new external request and a
new Model Governance reservation.
No historical reservation is deleted or rewritten.
## Acceptance
Attempt 4 must prove:
```text
scope max_input_tokens_per_request=16384
profile input_reservation_floor_tokens=16384
reserved_estimated_input_tokens>=16384
actual input <= reserved input
settlement=SETTLED
usage_source=runtime
audit_chain_valid=true
```
If attempt 4 exposes a new failure, stop. No additional retry extension is authorized by this
repair.
@@ -0,0 +1,211 @@
# AIOS Model Governance Runtime
## Status and purpose
This component turns the previously `PARTIAL` Model Governance interface map into an enforceable
runtime policy for the already verified Hermes/OpenClaw Online Agent path.
It is intentionally a **single SQLite library**, not a new network service. The existing external
Agent executor calls the library immediately before launching a model-backed runtime and settles the
same reservation immediately after the result.
The authoritative execution chain remains:
```text
Online Task scope / trace
→ dedicated worker claim + heartbeat
→ external Agent executor
→ ModelGovernanceStore.route()
→ root-owned runtime profile binds provider/model/version
→ Hermes / OpenClaw process
→ ModelGovernanceStore.record_success() / record_failure()
→ durable Online Agent checkpoint/result
```
P0 scope/trace/credential, P1 checkpoint/retry, P2 fleet scheduling and the existing external Agent
credential boundary remain authoritative.
## Ten Model Governance contracts
### MODEL_PROVIDER_REGISTRY
`model_governance_providers` stores provider identity, failure domain, circuit policy and an opaque
credential reference. Raw credentials are rejected.
Allowed reference prefixes:
- `systemd-credential:`
- `runtime-profile:`
- `credential-broker:`
### MODEL_SELECTION_POLICY
`model_governance_scope_policies` constrains eligible models/providers/capabilities. Routing is
deterministic: explicit preferred models, then configured priority, then stable provider/model IDs.
### MODEL_VERSION_PINNING
A registry row always includes a model version. A scope can additionally pin a model ID + exact
version. Every route decision persists both model ID and version plus model/policy revisions.
### MODEL_BUDGET_ACCOUNTING
A route first reserves estimated input/output tokens and estimated cost inside `BEGIN IMMEDIATE`.
Settled usage and outstanding reservations both count against the daily scope budget, preventing two
concurrent requests from oversubscribing the same budget.
Runtime-reported usage is preferred. When telemetry is absent, the reserved upper bound is charged
rather than silently under-counting.
OpenClaw's verified result shape is supported directly:
```text
meta.agentMeta.usage.input
meta.agentMeta.usage.output
meta.agentMeta.costUsd
```
Hermes currently has no structured usage in the verified output, so it uses conservative
reservation settlement.
### MODEL_FALLBACK_POLICY
Provider failures are durable. Repeated failures open a circuit for the configured cooldown. A
fallback request excludes the failed provider and increments `fallback_index`; the scope policy
bounds the number of fallback attempts.
### MODEL_SCOPE_ISOLATION
The signed `agent_runtime` execution envelope now carries authoritative Task scope + trace identity.
The projection layer always overwrites `runtime_input.scope` and `runtime_input.trace_id` from the
Online Task, even if an execution request attempts to supply different values.
Tenant/project lookup may fall back only to the same tenant's policy. It never falls back to a
system or legacy policy. `system` and `legacy` require their own explicit policies, avoiding fake
tenant identities.
### MODEL_CREDENTIAL_BOUNDARY
Model governance stores credential references only. The root-owned external-runtime profile still
owns the actual provider credential projection. Task payloads cannot select executable, argv,
environment, provider credential, or credential path.
The external child process remains unable to reach the AIOS control plane.
### MODEL_TRACE_AUDIT
Every route, failure, provider/model/policy update and settlement appends to a SHA-256 hash-chained
audit table with task ID, trace ID and scope key. `verify_audit_chain()` detects tampering.
### MODEL_EVALUATION_GATE
A model cannot enter the registry unless its state is `APPROVED` and it has a 64-hex evaluation
evidence digest.
The staging bootstrap uses the already verified Hermes/OpenClaw E2E external-result SHA256 values as
evaluation evidence, rather than a configuration-file hash.
### MODEL_ROUTING_RESILIENCE
Route request IDs are idempotent and bound to a canonical request hash. Conflicting replay is
rejected. Provider health, reservations, usage settlement and policy/provider/model updates use
transaction/CAS semantics.
## Runtime profile contract
A root-owned Hermes/OpenClaw profile may declare:
```json
{
"model_governance": {
"required": true,
"provider_ids": ["provider-a"],
"model_capabilities": ["text"],
"max_output_tokens": 512
}
}
```
When `required=true`:
- `provider_ids` must be non-empty;
- `max_output_tokens` must be positive;
- argv/environment must contain at least one governance-controlled marker:
`{provider_id}`, `{model_id}`, or `{model_version}`;
- `AIOS_MODEL_GOVERNANCE_ENABLED` must be enabled;
- a matching scope policy/model/provider must exist;
- budget reservation must succeed before the child process is launched.
The task cannot provide these markers or their values.
## SQLite authority
Default database:
```text
/var/lib/aios-model-governance/state.sqlite3
```
The executor process already owns the only runtime mutation path. The directory is created by
tmpfiles and is the only new writable path added to that executor.
No direct Task/Job/Git database writes are introduced.
## Bootstrap/reconciliation
`aios_model_governance_admin.py` applies a root-owned JSON bootstrap idempotently. It compares desired
state to current rows and uses the current revision as the CAS input only when a change is actually
required.
The repository staging bootstrap is not a Production policy. It exists only to reproduce and
govern the already verified non-production Hermes/OpenClaw runtimes.
## Cost caveat
The current verified OneAPI-backed runtime evidence reports zero authoritative provider cost for
OpenClaw and no structured cost for Hermes. The staging bootstrap therefore does **not** invent
pricing and sets cost rates/budget to zero/unbounded.
Token budgets remain enforced. Real monetary budget enforcement becomes effective as soon as
authoritative per-model pricing is entered into the root-owned registry.
## OpenClaw JIT/WASM compatibility
The previous live acceptance proved that OpenClaw Node/WASM requires
`MemoryDenyWriteExecute=no`. Model Governance must preserve that already-evidenced runtime exception;
it must not further weaken `NoNewPrivileges`, `ProtectSystem=strict`, `ProtectHome`, loopback deny,
credential isolation or repository isolation.
## Failure/recovery invariants
- Budget denial occurs before provider execution.
- Missing scope policy fails closed.
- Missing/disabled governance fails closed when a profile requires it.
- Actual usage cannot exceed the reservation.
- Provider failure releases that reservation into `FAILED` state before fallback.
- Settlement is idempotent.
- Route replay is idempotent only for the exact canonical request.
- Audit corruption is detectable.
- Online Agent checkpoint/result retry remains unchanged and remains the outer recovery authority.
## Verification
Required unit/integration coverage includes:
- registry/evaluation/CAS;
- scope isolation and no system→tenant fallback;
- model/version pinning;
- atomic budget reservation;
- settlement/replay;
- missing telemetry conservative settlement;
- provider circuit/fallback;
- trace/audit tamper detection;
- authoritative scope/trace projection;
- task runtime-input scope override attack;
- root-owned provider/model binding;
- OpenClaw nested usage extraction;
- real Hermes/OpenClaw staging live tasks with Model Governance evidence.
The governed implementation is not complete without the unified all-modified-files bundle for the
candidate delta and the cumulative lineage bundle.
@@ -0,0 +1,80 @@
# OpenClaw Input Reservation Floor
## Problem
Model Governance originally estimated input reservation from the user-visible prompt only.
The verified OpenClaw runtime injects additional root-owned/system-controlled context including
workspace, skills and system instructions. In live acceptance the prompt estimate reserved only
153 input tokens while OpenClaw reported approximately 7,770 actual input tokens.
The settlement correctly failed closed with:
```text
MODEL_USAGE_EXCEEDS_RESERVATION
```
The provider call itself had succeeded. The defect was therefore pre-execution reservation
underestimation, not provider connectivity.
## Contract
A root-owned external Agent profile may now declare:
```json
{
"model_governance": {
"input_reservation_floor_tokens": 16384
}
}
```
The effective input reservation is:
```text
max(
byte-based prompt estimate,
profile input_reservation_floor_tokens
)
```
Only the root-owned runtime profile can choose this floor. Online Task payloads cannot lower or
override it.
The OpenClaw staging profile uses 16,384 tokens. This is greater than twice the observed 7,770-token
live input high-water mark while remaining below the registered 32,768-token model input limit.
Hermes has no floor unless explicitly configured and therefore preserves its existing behavior.
## Fail-closed behavior
The floor is not an unlimited post-execution top-up.
If actual runtime usage exceeds the reserved amount, settlement still fails with
`MODEL_USAGE_EXCEEDS_RESERVATION`.
This keeps budget enforcement pre-execution and avoids silently authorizing usage after it has
already happened.
## Evidence
External results expose:
```text
prompt_estimated_input_tokens
input_reservation_floor_tokens
reserved_estimated_input_tokens
```
alongside the normal settlement and audit evidence.
## Retry
The current OpenClaw attempt 1 failure remains preserved. After this code repair is governed and
activated, official `NEEDS_REWORK` returns the same fixed Task to the normal retry path. Because AR1
already made external request identity attempt-scoped, attempt 2 receives a new external request ID
and a new reservation.
No retry-extension is required because the Task is currently attempt 1 of 3.
No Model Governance reservation is deleted or rewritten.
@@ -0,0 +1,212 @@
# AIOS Worker Availability Runtime
## Priority
This runtime exists to make the already verified Online Agent execution path continuously usable
without repeated human intervention.
The system already proved that generic, Hermes and OpenClaw workers can claim and complete real
Online Tasks. The remaining availability defect was operational: evidence-freeze workflows left the
worker services stopped, while registry `ACTIVE` records remained stale.
The availability contract therefore separates:
```text
AUTO_CLAIM_MECHANISM=VERIFIED
CURRENT_WORKER_AVAILABILITY=<fresh runtime state>
```
A historical successful claim is not current liveness.
## Source of truth for idle liveness
An idle worker may have no Task lease heartbeat. Therefore Task heartbeat alone cannot prove that
the worker is continuously polling.
`aios_online_agent_worker.py` now writes:
```text
<AIOS_WORKER_STATE_ROOT>/worker-liveness.json
```
on:
- process start: `STARTING`;
- every successful poll: `POLL_OK`;
- poll failure: `POLL_FAILED`;
- every successful Task lease heartbeat while executing: `BUSY`.
The file contains:
- agent ID/type/version;
- process PID;
- monotonically increasing local sequence;
- `last_seen_epoch`;
- poll/heartbeat intervals;
- current Task/stage while busy.
The liveness file is worker-owned observability state only. It does not write or mutate Task, Job,
Git, WriterLease or registry databases.
## Self-healing guard
`aios-worker-availability-guard.timer` invokes a root-owned oneshot guard every 30 seconds.
The guard manages:
```text
aios-online-agent-worker-20260815.service
aios-hermes-online-agent-worker.service
aios-openclaw-online-agent-worker.service
```
and the required external runtime dependency:
```text
aios-external-agent-executor.service
```
For each worker:
1. missing systemd unit → fail closed;
2. disabled → `systemctl enable`;
3. inactive → `systemctl start`;
4. active but liveness older than 90 seconds → restart;
5. repeated stale restart within 60 seconds → do not restart again; report degraded.
Long-running Tasks are safe because the Task lease-heartbeat thread refreshes `BUSY` liveness.
The guard never claims a Task and never touches Task/Job/Git databases.
## Maintenance inhibit
Planned maintenance creates:
```text
/run/aios-worker-availability/maintenance.lock
```
While this file exists the guard performs no start/restart actions and reports:
```text
MAINTENANCE_INHIBITED
```
Maintenance tooling must remove the file when done. This prevents the self-healer from fighting
intentional service changes.
## Status
The guard writes:
```text
/run/aios-worker-availability/status.json
```
with current worker/dependency states, liveness ages and recovery actions.
A worker is currently available only when:
```text
systemd load_state=loaded
systemd active_state=active
unit enabled/static
worker-liveness.json present
last_seen_age <= 90s
```
Registry `ACTIVE` without fresh poll/busy liveness is not sufficient.
## Boot/restart behavior
The timer is:
```text
OnBootSec=15s
OnUnitActiveSec=30s
Persistent=true
```
Therefore reboot, manual stop left behind by another workflow, process crash, and hung poll loop are
all repaired without requiring a human to notice and intervene.
Normal worker units retain their own `Restart=always`; the availability guard handles the cases that
`Restart=always` cannot, especially a deliberate `systemctl stop` left after an evidence workflow.
## Acceptance
The live acceptance requires all of the following:
```text
guard timer active/enabled
external executor active/enabled
generic worker active/enabled + fresh liveness
Hermes worker active/enabled + fresh liveness
OpenClaw worker active/enabled + fresh liveness
```
Then a new read-only Online Task is projected through canonical Bridge `18892`.
Success requires:
```text
TASK_CREATED
→ generic worker auto claim
→ TASK_HEARTBEAT
→ RESULT_SUBMITTED
→ official TASK_VERIFIED(SUCCEEDED)
```
After the Task, the services must remain active and liveness must continue advancing. The acceptance
also stops a worker intentionally once and proves the guard starts it again with a new fresh
liveness sequence.
Only after these gates may current status be upgraded from:
```text
OVERALL=PARTIAL
```
to:
```text
PERSISTENT_WORKER_LIVENESS=VERIFIED
NEW_TASK_AUTO_CONSUMPTION=VERIFIED
WORKER_AVAILABILITY_RUNTIME=VERIFIED_CANDIDATE
```
Protected main and Production remain out of scope.
## Forced source-capture projection compatibility
The deployed canonical Bridge currently projects a default `source_capture` envelope even when a
governed binding request asks for binding-only behavior. This must not be allowed to make always-on
workers consume engineering Tasks before their deterministic plan is registered.
The worker therefore distinguishes a real read-only source capture from an engineering placeholder:
```text
source_capture
+ capability_scope contains governed.write.branch or governed.write.file
= PENDING_DETERMINISTIC_PLAN
```
For such a placeholder the worker:
1. re-resolves through the canonical plan registry **without replaying the stale source-capture
envelope**;
2. if no deterministic plan exists, emits `TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN` and does not
claim;
3. if the registered canonical envelope is deterministic and the governed binding still matches,
uses that deterministic envelope and claims normally.
A real read-only source-capture Task with only `git.read`/`git.test` still replays its exact persisted
payload and remains claimable.
This makes persistent workers safe with the currently deployed Bridge while preserving the same
Task/Job binding. No Task DB rewrite, cancellation, replacement Task, fake NEEDS_REWORK, or
pre-claim metadata mutation is required.
A later Bridge cleanup may implement true binding-only projection, but Worker Availability does not
depend on that cleanup.
@@ -0,0 +1,96 @@
# Canonical all-modified-files packager and secret scanning
## Problem
The historical all-modified-files evidence packager lived only as a copied handoff script.
Its private-key scanner matched the fragment:
```text
BEGIN PRIVATE KEY
```
instead of a complete PEM header. A regression test containing that literal therefore caused a
false secret finding even though the candidate contained no credential.
A task had to use a temporary evidence-only compatibility packager to complete its bundle. That is
acceptable as an emergency workaround but not as a durable unattended path.
## Repository source of truth
The canonical implementation is now:
```text
active-runtime/app/aios_all_modified_files_packager.py
```
It preserves the historical output/bundle schemas:
```text
aios.all-modified-files.bundle.v1
aios.all-modified-files.bundle-summary.v1
aios.modified-documents.index.v1
```
and the host-visible status lines:
```text
ALL_MODIFIED_FILES_BUNDLE=PASS
ALL_MODIFIED_FILES_BUNDLE_PATH=...
ALL_MODIFIED_FILES_BUNDLE_SHA256=...
```
Future governed tasks should use this repository version instead of copying an unversioned scanner.
## Secret scanning invariant
Private-key detection requires a complete PEM header:
```text
-----BEGIN [RSA|EC|DSA|OPENSSH ]PRIVATE KEY-----
```
The following source/test/document text is not secret material and must remain packageable:
```text
BEGIN PRIVATE KEY
PRIVATE KEY
PEM private-key header
```
Bearer and key/value credential detection remains fail-closed:
```text
Authorization: Bearer <20+ token chars>
password=<12+ non-space chars>
passwd=<12+ non-space chars>
secret=<12+ non-space chars>
api_key=<12+ non-space chars>
api-key=<12+ non-space chars>
```
Tests construct the real PEM header dynamically so the scanner's own test source does not contain a
literal credential-looking header.
## Bundle integrity
The packager continues to enforce:
- exact base→candidate ancestry;
- before/after file capture;
- rename/copy/delete handling;
- path traversal denial;
- execution evidence symlink denial;
- secret scan over repository before/after and execution evidence;
- document index;
- SHA-256 closure;
- ZIP without symlinks.
No secret finding is downgraded to a warning.
## Migration
The old handoff script may remain for historical reproducibility, but new handoffs should call the
repository packager from the verified candidate lineage.
A temporary compatibility scanner is no longer needed once this candidate is in the lineage.
@@ -0,0 +1,210 @@
# AIOS Autonomous Control Plane — Admission + Invariant + Repair
## Why this exists
Recent Agent registration, Task claim, release-worker, credential and retry failures shared one
failure mode:
> The control plane accepted an intent before the runtime had proved it could execute the intent.
This design does not add another fleet of daemons. It adds one shared library contract that Task API,
workers and governed host recovery consume.
## Three layers
```text
Intent
↓
Runtime Admission
↓
System Invariants
↓
Repair Policy
↓
Execution
```
### Runtime Admission
Admission occurs before Task claim.
A capability such as `release:candidate` is not considered executable merely because its name
matches. The capability contract also binds:
- supported contract version;
- allowed permission;
- forbidden operations;
- runtime dependencies;
- health requirements.
For `release:candidate`:
```text
permission:
prepare_release
forbidden:
production_deploy
submit_owner_approval
tag_publish
baseline_registry_finalize
runtime_switch
dependencies:
credential_projection
trusted_builder
health:
authenticated_probe
release_api_health
worker_liveness
```
Only a fully passing decision reaches:
```text
EXECUTION_READY
```
A failed decision remains:
```text
EXECUTION_READY_FAILED
```
and the worker does not call `/claim`.
Therefore an infrastructure defect discovered by admission does not consume a business attempt.
### System Invariants
The same admission checks are exposed as a machine-readable invariant snapshot.
Examples:
```text
capability_contract_registry
task_capabilities_registered
worker_capabilities_registered
capability_match
forbidden_operations_absent
credential_projection
trusted_builder
authenticated_probe
release_api_health
worker_liveness
```
A runtime can expose:
```text
HEALTHY
DEGRADED
```
without relying on a human reconstructing state from logs.
### Repair Policy
Failure classification maps to bounded recovery:
```text
DEPENDENCY_UNAVAILABLE → level 0 / auto local
RESOURCE_UNAVAILABLE → level 0 / auto local
WORKER_CRASH → level 0 / auto local
LEASE_LOST → level 0 / auto local
CAPABILITY_CONTRACT_ERROR → level 1 / same-lineage repair task
CREDENTIAL_PROJECTION_ERROR → level 1 / same-lineage repair task
UNKNOWN → level 1 / diagnostic repair task
POLICY_DENIED → level 2 / Owner authorization
BUSINESS_PERMANENT → level 3 / terminal/manual decision
```
Only business-permanent failure consumes the business retry budget in this first contract.
The current Task API does not yet have a first-class `EXECUTION_READY_FAILED` durable state. Until it
does, the Release Worker enforces the most important invariant by refusing to claim a Task whose
admission fails.
## Capability Contract Registry
`aios_capability_contract.py` remains backward-compatible with existing aliases but now validates
every alias target against a versioned semantic contract registry.
Unknown capabilities and wildcard capabilities remain fail-closed.
Agent registration and Task creation should next be changed to call `registered_capability()` so a
capability cannot be accepted at registration and rejected only later by the matcher.
## Credential rule
Admission probes credential usability, not merely credential existence.
For release candidate creation the authenticated probe intentionally sends an invalid empty
`/createReleaseCandidate` request.
Expected:
```text
HTTP 400
```
This proves:
```text
credential readable
→ authentication succeeded
→ scope accepted
→ request validation rejected empty business input
```
A permission failure/401/403/5xx is `EXECUTION_READY_FAILED`.
## Lineage
Repair actions are descriptive, not permission grants. Any repair Task must retain the existing
root/parent/attempt/authority chain and use governed same-lineage recovery.
No repair action may silently create a replacement business Task.
## Authority separation
Capability answers:
```text
can this runtime perform the operation?
```
Authority answers:
```text
is this operation currently authorized?
```
`release:candidate` never implies Owner approval, deployment, tag publication, baseline finalization
or runtime switch.
## Deployment sequence
Phase 1 (this candidate):
- Capability Contract Registry;
- shared Autonomous Control Plane library;
- Release Worker pre-claim admission;
- failure classification + repair evidence.
Phase 2:
- Task API uses the same library before returning Tasks from poll and again during claim CAS;
- Task schema gains durable `ADMITTED/EXECUTION_READY/EXECUTION_READY_FAILED` evidence without
counting pre-side-effect infrastructure failures against business attempts.
Phase 3:
- Agent registration validates against the same Capability Contract Registry;
- external connector writes only durable Intent/Command Inbox records and leaves execution to the
local control plane.
This sequencing avoids a second controller stack while converging every decision onto one contract.
@@ -0,0 +1,228 @@
# Durable Owner Authorization and capability projection
## Problem
AIOS already supports unattended worker execution after a Task is executable, but Owner intent was
not durable at the capability layer. The same Owner repeatedly had to authorize or repair:
- `governed-task:project`;
- `release:candidate`.
This caused valid one-time Owner intent to be lost between conversations and forced repeated human
interaction.
## Design
Durable authorization is represented by a root-owned mode `0600` policy outside Git. The policy is
persistent until explicitly revoked.
The first persistable capabilities are deliberately narrow:
```text
governed-task:project
release:candidate
```
They cover preparation work only.
The following capabilities can never be inherited from this durable grant:
```text
submitOwnerApproval
startDeployment
tag_publish
baseline_registry_finalize
runtime_switch
main_write
production_deploy
force_push
credential_admin
```
Those remain separate high-risk release/Owner-signature gates.
## Policy
Example schema:
```json
{
"schema": "aios.owner-durable-authorization.v1",
"grant_id": "owner-permanent-preparation-20260819",
"owner_identity": "owner@example",
"account": "chatgpt-governed-implementer-singlehost",
"capabilities": [
"governed-task:project",
"release:candidate"
],
"issued_at": "2026-08-19T05:00:00Z",
"not_before": "2026-08-19T05:00:00Z",
"persistent_until_revoked": true,
"constraints": {
"repository": "aios/aios",
"allowed_target_prefix": "agent/",
"release_version_prefix": "V2R2C",
"max_task_risk": "engineering"
}
}
```
The repository never stores the live Owner policy or release credential.
## Revocation
A root-owned mode `0600` revocation file lists `revoked_grant_ids`.
Revocation is checked on every operation, not only at service startup. A running broker therefore
fails closed immediately after revocation without needing a new conversation.
## Broker boundary
The broker exposes only an AF_UNIX socket:
```text
/run/aios-owner-authorization-broker/broker.sock
```
The socket is mode `0660` and owned by the service's `aios-executor-exchange` group. This avoids introducing another long-lived bearer secret solely for local broker access.
It exposes only:
```text
GET /v1/capabilities
POST /v1/project-governed-task
POST /v1/authorize-release-candidate
```
`project-governed-task` calls only the canonical Bridge.
`authorize-release-candidate` persists an Owner authorization decision only. It deliberately does
**not** invent a local trusted-builder endpoint. The external release connector remains responsible
for projecting the account's real `release:candidate` scope. Until that external projection exists,
the decision state is `AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION`.
The broker holds only the existing host control credential needed for canonical governed-task
projection. It never holds an Owner signing key or a release/deployment credential. The caller also supplies the expected account identifier over the Unix transport; the OS socket permission is the local access boundary.
## Canonical Online Task projection
A durable `governed-task:project` grant may create a complete canonical Online Task rather than a
bare Task. It requires:
```text
require_online_task_projection=true
generation=1
branch_mode=CREATE_NEW
max_attempts<=3
risk<=engineering
initial action=source_capture
```
Projected task capabilities are limited to:
```text
git.read
git.test
governed.write.branch
governed.write.file
```
A write-capable task still begins as `source_capture`. Existing placeholder-deferral prevents the
generic worker from claiming it until a deterministic plan is registered. Repository mutation
therefore still requires the normal deterministic plan, WriterLease and governed commit path.
This fixes the previous split-brain behavior where a bare Task could exist without the canonical
governed execution projection.
## External capability projection outbox
A `release:candidate` authorization now creates a durable machine-readable outbox record:
```text
schema=aios.external-capability-projection.request.v1
state=PENDING_EXTERNAL_CONNECTOR
capability=release:candidate
```
The projection identifier is deterministic from:
```text
grant_id
account
capability
request_sha256
```
The outbox stores no credentials or Owner signing material. It retains only the already-authorized
release-candidate request, policy/request hashes, account, grant id and timestamps.
The broker exposes read-only Unix-socket endpoints:
```text
GET /v1/projections
GET /v1/projections/{projection_id}
```
These endpoints re-check the current Owner policy on every read. If the durable grant has been
revoked, enumeration fails closed with `OWNER_GRANT_REVOKED`. The historical outbox record is
retained for audit and is not silently deleted.
The repository intentionally does **not** provide a connector acknowledgement/write endpoint yet.
No in-repository code can truthfully assert that the external ChatGPT connector account has gained
`release:candidate`. Until the platform consumes the outbox and performs its own identity-scope
projection, the only valid state is:
```text
AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION
PENDING_EXTERNAL_CONNECTOR
```
A future external projector must preserve:
```text
release:candidate
!= submitOwnerApproval
!= startDeployment
!= tag_publish
!= baseline_registry_finalize
!= runtime_switch
```
## Idempotency and audit
Every broker operation requires an idempotency key. A receipt SQLite database stores only:
- idempotency key;
- capability;
- request SHA-256;
- response SHA-256;
- sanitized upstream response;
- timestamp.
An exact retry returns the same receipt. Reusing an idempotency key with different content fails.
## Unattended invariant
Once the Owner policy and narrow service credentials are provisioned:
```text
Owner durable grant
→ broker decision
→ canonical governed task projection / release candidate preparation
→ worker / trusted builder
```
No repeated Owner interaction is required for those preparation capabilities.
Production execution remains:
```text
release candidate
→ immutable approval envelope
→ Owner signature
→ deployment
```
The durable grant never substitutes for the Owner signature.
@@ -0,0 +1,114 @@
# Owner-approved GPT HTTPS edge activation and rollback
## Scope
This controller is the final host-side control mechanism before the disabled-by-default GPT HTTPS
edge may bind publicly.
Repository implementation and public activation remain separate states:
```text
IMPLEMENTED
≠
OWNER_APPROVED
≠
ACTIVATED
≠
RUNTIME_VERIFIED
```
The controller never creates owner approval, TLS assets, DNS, firewall rules or ingress bearer
credentials.
## Approval binding
The owner approval file is root-owned mode `0600` and has schema:
```json
{
"schema": "aios.gpt-https-edge.public-approval.v1",
"approval_id": "owner-public-edge-...",
"candidate_oid": "<exact governed edge candidate>",
"domain": "tasks.example.com",
"public_bind": true,
"bind_host": "0.0.0.0",
"port": 18443,
"tls_cert_sha256": "<exact certificate sha256>",
"issued_at": "2026-08-19T00:00:00Z",
"not_before": "2026-08-19T00:00:00Z",
"expires_at": "2026-08-20T00:00:00Z",
"owner": "<owner identity>"
}
```
Approval lifetime is at most seven days. Candidate/domain/bind/port/certificate are immutable within
one approval.
Changing any of those requires a new owner approval.
## Check
```text
aios_gpt_https_edge_activation.py check --candidate <GE>
```
Checks:
- approval ownership/mode/schema/time window;
- exact candidate binding;
- exact certificate hash;
- certificate/private-key pair;
- certificate hostname;
- certificate remaining lifetime;
- loopback GPT ingress service health.
It performs no public network mutation.
## Activate
`activate` additionally:
- writes one exact systemd public-bind drop-in atomically;
- enables/restarts only the HTTPS edge unit;
- verifies effective ExecStart points at the immutable candidate release;
- verifies effective public-bind/domain environment;
- performs a TLS handshake to `127.0.0.1:18443` using the approved public hostname;
- writes a durable activation receipt.
Any failure after drop-in mutation automatically invokes rollback.
Exact replay of an already-active matching approval returns the durable receipt without restarting
or creating duplicate state.
## Rollback
`rollback`:
- stops and disables only the HTTPS edge;
- removes only the controller-owned public-bind drop-in;
- daemon-reloads systemd;
- verifies edge inactive;
- writes a durable rollback receipt.
It does not delete:
- owner approval;
- TLS certificate/private key;
- ingress bearer token;
- internal loopback ingress;
- Task/Job state;
- Git branches.
Rollback is therefore replay-safe and does not require reconstructing secrets.
## Not covered
This controller intentionally does not own:
- DNS records;
- host firewall/NAT/security-group changes;
- certificate issuance/renewal;
- OpenAI Custom GPT domain verification;
- public Internet acceptance from outside the host.
Those remain separate owner/environment gates.
@@ -0,0 +1,120 @@
# Disabled-by-default HTTPS edge for external GPT Online Task access
## Purpose
The internal ingress (`127.0.0.1:18945`) remains loopback-only. This edge is the only component
allowed to become Internet-facing, and only after a separate owner approval.
It forwards only:
```text
POST /v1/read-only-tasks
GET /v1/read-only-tasks/{request_id}
GET /openapi.json
GET /health
```
It never proxies:
```text
18892 canonical Bridge
18940 Task API
WriterLease
commit
verification
release
tag
baseline
arbitrary URLs
```
## Default state
The systemd unit is disabled by policy unless the owner creates:
```text
/etc/aios-gitops/gpt-https-edge.approved
```
The base unit still binds only to:
```text
127.0.0.1:18443
AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=0
```
Non-loopback bind is rejected by code unless an owner-controlled drop-in sets:
```text
AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=1
AIOS_GPT_EDGE_HOST=0.0.0.0
AIOS_GPT_EDGE_PUBLIC_DOMAIN=<approved hostname>
```
This repository candidate does not create that drop-in or approval marker.
## TLS
TLS certificate/key are owner-managed systemd credentials:
```text
/etc/aios-gitops/gpt-https-edge-tls.crt
/etc/aios-gitops/gpt-https-edge-tls.key
```
The service requires TLS 1.2 or newer, disables TLS compression, and never logs or returns key
material.
Certificate/key issuance and renewal are external owner responsibilities until a separately
governed ACME workflow is implemented.
## Request protections
The edge provides:
- 64 KiB request cap;
- 256 KiB upstream response cap;
- JSON validation before proxying;
- fixed upstream `http://127.0.0.1:18945`;
- Authorization-only header forwarding;
- no Cookie/X-Forwarded-* trust;
- route/method allowlist;
- per-source-IP in-memory rate limiting;
- bounded concurrent request handling;
- optional exact Host-header enforcement;
- HSTS / no-store / nosniff / no-referrer response headers.
The rate limiter is intentionally simple and local. Multi-node/global abuse protection is outside
the current one-host architecture.
## Authentication
The HTTPS edge does not create a new authentication scheme. It forwards the external Bearer
credential to the loopback GPT ingress, which verifies `gpt_ingress_token`.
The edge never receives the AIOS control-plane token.
## Deployment boundary
Public activation is a high-risk network operation and requires explicit owner approval. Before
activation verify:
```text
host-side GPT ingress E2E=VERIFIED
owner ingress token provisioned
TLS certificate/key provisioned
public hostname owned and DNS configured
firewall/network exposure reviewed
explicit public-bind approval recorded
rollback plan available
```
Only after those gates may an owner-authorized runtime drop-in bind this service publicly.
Until then:
```text
GPT_HTTPS_EDGE_IMPLEMENTATION=candidate
PUBLIC_NETWORK_EXPOSURE=NONE
PUBLIC_CUSTOM_GPT_DIRECT_AI0S_ACCESS=NOT_VERIFIED
```
@@ -0,0 +1,138 @@
# External GPT → Online Task ingress
## Scope
This component closes the current architectural gap between an external Custom GPT and AIOS
without exposing the internal Task API (`18940`) or canonical Bridge (`18892`) directly.
The first supported public-facing capability is intentionally read-only:
```text
authenticated external intent
→ host-side ingress
→ canonical /v2/governed-task
→ canonical /v2/execution-envelope
→ persistent generic worker
→ source_capture result
→ official control-plane verification
```
The ingress itself never claims, heartbeats, verifies, commits, acquires WriterLease, or writes
protected refs.
## Security boundary
The service binds only to loopback (`127.0.0.1:18945`). A future owner-approved TLS reverse proxy
or tunnel may expose this service externally. Direct non-loopback binding is denied by code.
Two independent credentials are projected by systemd:
- `gpt_ingress_token`: authenticates the external caller to this ingress.
- `agent_task_control_token`: lets the ingress call the loopback AIOS control plane.
Neither credential is stored in Git, SQLite, task payloads, results, logs, or API responses.
The ingress has no repository filesystem access.
## Caller-controlled fields
Allowed:
```text
request_id
goal
instructions
success_criteria
constraints.max_files
constraints.focus_paths
constraints.notes
```
Not caller-controlled:
```text
repository
base_oid
expected_branch_oid
target_branch
branch_mode
generation
risk
priority
max_attempts
capabilities
action/adapter
WriterLease
worker identity
credentials
verification decision
main/production/tag/baseline permissions
```
All external tasks are hard-bound to:
```text
repository=aios/aios
capabilities=[git.read, git.test]
action=source_capture
writer_lease=false
repository_mutation=forbidden
control_plane_side_effects=forbidden
credential_access=forbidden
```
## Idempotency
`request_id` is the durable external idempotency boundary.
The ingress stores only:
```text
request hash
derived AIOS task id
canonical job id
base oid
sanitized receipt
state
```
An exact retry returns the same receipt. Reusing a `request_id` with different content returns
`IDEMPOTENCY_CONFLICT`.
A crash after canonical task creation can recover the same binding from the Task API rather than
creating a replacement Task.
## Status
`GET /v1/read-only-tasks/{request_id}` returns only sanitized state:
```text
task/job ids
status
attempt
whether claimed
result classification
official verification decision/classification
source snapshot hash/count when available
```
It does not proxy raw Task metadata, execution envelopes, result text, credentials, or source files.
## Public exposure
This candidate does **not** make AIOS publicly reachable. Public exposure requires a separately
approved HTTPS ingress/reverse proxy with:
- TLS;
- external authentication lifecycle;
- rate limiting / abuse controls;
- request size limits;
- observability;
- explicit owner approval for the network exposure.
Until that step is verified:
```text
HOST_SIDE_EXTERNAL_GPT_ADAPTER=implemented/staging-candidate
PUBLIC_CUSTOM_GPT_DIRECT_AI0S_ACCESS=not verified
```
@@ -0,0 +1,87 @@
# Release candidate capability contract remediation
## Problem
The release-preparation worker can be correctly registered with the raw capability:
```text
release:candidate
```
while the canonical Task API matcher still rejects the worker.
Before this repair, the capability matrix is:
```text
worker raw = ["release:candidate"]
worker effective= []
task required = ["exact:release:candidate"]
eligible = false
```
The Task therefore remains `QUEUED`, `attempt=0`, and cannot be claimed even though both the Task and
worker use the same literal capability.
## Root cause
`aios_capability_contract.py` is deliberately fail-closed:
- task-side unknown names become `exact:<name>`;
- worker-side unknown names are discarded;
- wildcard worker capabilities are ignored.
`release:candidate` was absent from both canonical alias tables.
## Repair
Add exactly one semantic capability to both vocabularies:
```text
release:candidate → release:candidate
```
Do not add:
```text
release:*
submitOwnerApproval
startDeployment
tag_publish
baseline_registry_finalize
runtime_switch
```
Unknown names and wildcards must continue to fail closed.
## Runtime recovery
After the governed remediation candidate is officially verified:
1. install the candidate capability-contract module as a higher-precedence Task API overlay;
2. restart only `aios-agent-task-api.service`;
3. require `/health` success;
4. require the fixed existing release Task to remain the same Task/idempotency;
5. let the already-active dedicated release worker poll and claim it automatically;
6. require heartbeat, trusted-builder result and official verification;
7. do not manually claim or create a replacement live Task.
The fixed V2R2C2 live Task may complete as the acceptance candidate it was originally bound to.
Because this remediation is a later source commit, the first release candidate that includes the
matcher repair should use the next immutable version (for example `V2R2C3`) rather than rewriting an
already-created V2R2C2 candidate.
## Governance boundary
This repair affects only Task eligibility. It grants no credential and performs no release action by
itself.
The dedicated release worker still requires its independent `release:candidate` credential, and the
following remain separate:
```text
Owner signed approval
deployment
tag publication
baseline finalization
runtime switch
```
@@ -0,0 +1,82 @@
# Fixed V2R2C2 release lineage contract
## Why this contract exists
AIOS has multiple engineering commits in the same ancestry, and they serve different purposes.
The fixed V2R2C2 live Task was created by the dedicated Release Worker activation. Its immutable
release source is therefore the Release Worker implementation candidate:
```text
71e736b05fab13efc06af7a4fe2dc022c1cf9a85
```
Later engineering commits are descendants but are not retroactive V2R2C2 release sources:
```text
17deb91ca7a187dc81766945b1174e854f86bd25
capability-contract remediation
b4b4416c93fedbe451d655d49e28f915b8fd38e6
Autonomous Control Plane engineering candidate
```
Conflating "current engineering base" with "immutable release source" caused ACP activation to
fail closed with `FIXED_RELEASE_SOURCE_DRIFT`.
## Canonical fixed lineage
```text
Task:
r42-release-candidate-v2r2c2-live-20260819
candidate_id:
r42-release-candidate-v2r2c2-20260819
source_commit:
71e736b05fab13efc06af7a4fe2dc022c1cf9a85
version:
V2R2C2
required_capabilities:
["release:candidate"]
```
This contract is immutable for V2R2C2.
## What may change
The following are lifecycle state, not release identity:
```text
status
attempt
max_attempts
revision
claimed_by
lease
verification
```
They may change through official retry/claim/result/verification without changing the release
lineage.
## What must never be "repaired"
Do not rewrite the fixed V2R2C2 Task source to a later engineering commit.
A later engineering source that must itself be released receives a later immutable release version
(for example V2R2C3), not a mutation of V2R2C2.
## Activation invariant
ACP activation must:
1. verify its own governed engineering candidate separately;
2. read the live fixed V2R2C2 Task;
3. validate that Task through `validate_fixed_v2r2c2_task()`;
4. never compare the fixed release source to the ACP engineering base/candidate;
5. never mutate the fixed Task merely to satisfy activation.
This makes engineering lineage and release lineage explicit and independently verifiable.
@@ -0,0 +1,142 @@
# Release Preparation Online Agent
## Purpose
This worker closes the release-preparation execution gap without widening the ChatGPT connector
identity.
The dedicated Agent identity is:
```text
online-release-worker-cn-network-mkt-20260819
```
Its only advertised task capability is:
```text
release:candidate
```
The worker may create an immutable release candidate, but it never submits Owner approval and never
starts deployment, publishes a tag, finalizes a baseline, switches runtime or writes Git.
## Credentials
The worker uses two independently scoped credentials:
```text
agent_token
release_candidate_token
```
`agent_token` is the Online Agent registration credential. It must be encrypted into systemd's
credential store and must never be written to Git, Task metadata, evidence or logs.
`release_candidate_token` is provisioned during Owner-authorized runtime activation. Only its SHA-256 record is stored in the release API token registry; the plaintext is immediately encrypted into systemd credstore and the temporary plaintext is removed. Its only allowed scope is:
```text
release:candidate
```
It is explicitly not an Owner signing key and must not grant:
```text
submitOwnerApproval
startDeployment
tag_publish
baseline_registry_finalize
runtime_switch
```
The unit reads both credentials through systemd `LoadCredentialEncrypted`; neither plaintext token is kept in the unit, repository, Task metadata, evidence, or logs.
## Task contract
The worker will only claim Tasks whose exact required capability set is:
```json
["release:candidate"]
```
The Task must provide a bounded `constraints.release_candidate` payload (metadata is accepted only for compatibility):
```json
{
"candidate_id": "...",
"source_commit": "<40 hex>",
"version": "V2R2C2",
"projection_id": "projection-..."
}
```
No caller-supplied URL, method, headers, command, shell, environment, credential path, deployment
operation or arbitrary release route is accepted.
The release API base is host configuration, not Task input. The only release mutation invoked by the
worker is:
```text
POST /createReleaseCandidate
```
## Lifecycle
```text
poll
→ claim
→ heartbeat
→ createReleaseCandidate
→ result submit
→ official verifier
```
The result declares:
```text
writer_lease=NONE
changed_files=[]
direct_main_write=false
production_write=false
tag_write=false
baseline_write=false
runtime_switch=false
owner_approval_submitted=false
```
## Remaining high-risk gate
Successful release-candidate creation does not authorize Production.
The next stage remains:
```text
release candidate
→ immutable approval envelope
→ Owner signature
→ startDeployment
```
That separation is mandatory even when release preparation is fully unattended.
## Unattended availability
The release worker has its own systemd watchdog rather than modifying the shared worker-availability
guard contract.
Every successful Task poll and Task heartbeat emits:
```text
WATCHDOG=1
```
and the service uses:
```text
Type=notify
WatchdogSec=90
Restart=always
```
A process that is technically alive but no longer polling/heartbeating therefore gets restarted by
systemd. The worker also writes `worker-liveness.json` for operator diagnostics.
+1
View File
@@ -0,0 +1 @@
placeholder
@@ -0,0 +1,66 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
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,
)
@pytest.mark.parametrize("runtime", ["hermes", "openclaw"])
def test_runtime_capability_is_known_to_task_and_worker_vocabulary(runtime: str) -> None:
capability = f"agent.runtime.{runtime}"
assert normalize_required_capabilities([capability]) == frozenset({capability})
assert normalize_worker_capabilities([capability]) == frozenset({capability})
assert has_capabilities([capability], [capability]) is True
def test_runtime_capabilities_are_isolated_between_workers() -> None:
assert has_capabilities(["agent.runtime.hermes"], ["agent.runtime.openclaw"]) is False
assert has_capabilities(["agent.runtime.openclaw"], ["agent.runtime.hermes"]) is False
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
@@ -0,0 +1,452 @@
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,
InitialExecutionProjectionRequest,
InitialExecutionProjectionStore,
SourceCaptureProjectionRecoveryRequest,
)
from aios_online_agent_execution import (
GovernedExecutionBinding,
project_execution_envelope,
)
AUTHORIZATION="owner-initial-execution-projection-recovery-20260820"
TASK_ID="task-initial-projection-a"
JOB_ID="job-initial-projection-a"
BASE="a"*40
BRANCH="agent/task-initial-projection-a"
IDEMPOTENCY="task-initial-projection-binding-v1"
CAPABILITY="agent.runtime.openclaw"
@pytest.fixture(autouse=True)
def _auth(monkeypatch):
monkeypatch.setenv(
"AIOS_AGENT_TASK_INITIAL_EXECUTION_PROJECTION_AUTHORIZATION_ID",
AUTHORIZATION,
)
def canon(value):
return hashlib.sha256(
json.dumps(value,sort_keys=True,separators=(",",":")).encode()
).hexdigest()
def binding():
return GovernedExecutionBinding.from_mapping({
"task_id":TASK_ID,
"job_id":JOB_ID,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":BRANCH,
"branch_mode":"CREATE_NEW",
"generation":1,
"idempotency_key":IDEMPOTENCY,
})
def runtime_input():
return {
"goal":"Supervise release preparation",
"instructions":"Observe and resume only the same lineage.",
"success_criteria":[{"id":"heartbeat","description":"Heartbeat observed"}],
"constraints":{"production_write":False},
"scope":{"scope_type":"legacy","tenant_id":None,"project_id":None},
"trace_id":TASK_ID,
}
def envelope(*, job_id=JOB_ID, action="agent_runtime", capability=CAPABILITY):
bound=binding()
if job_id != JOB_ID:
bound=GovernedExecutionBinding.from_mapping({**bound.public(),"job_id":job_id})
return project_execution_envelope(
bound,
{
"action":action,
"adapter":"openclaw" if action=="agent_runtime" else "source_capture",
"capability_scope":[capability],
"runtime_input":runtime_input() if action=="agent_runtime" else None,
},
)
def make_db(tmp_path, *, claimed_by=None, lease=False, metadata=None, caps=None, event="TASK_CREATED"):
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 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
);
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
);
""")
metadata={} if metadata is None else metadata
caps=[] if caps is None else caps
con.execute(
"INSERT INTO tasks VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(
TASK_ID,"Supervise release preparation",
"Observe and resume only the same lineage.",
json.dumps([{"id":"heartbeat","description":"Heartbeat observed"}]),
json.dumps(caps),json.dumps({"production_write":False}),json.dumps(metadata),
None,100,20,0,"QUEUED",claimed_by,
"lease-hash" if lease else None,
"2099-01-01T00:00:00Z" if lease else None,
None,None,None,None,1,"2026-08-20T00:00:00Z","2026-08-20T00:00:00Z",
),
)
con.execute(
"INSERT INTO events(task_id,agent_id,event_type,payload_json,created_at) VALUES (?,?,?,?,?)",
(TASK_ID,None,event,"{}","2026-08-20T00:00:00Z"),
)
con.commit();con.close()
return path
def request(*, digest=None, key="initial-projection-a-v1"):
return InitialExecutionProjectionRequest.from_mapping({
"expected_status":"QUEUED",
"expected_revision":1,
"expected_attempt":0,
"expected_metadata_sha256":canon({}),
"expected_required_capabilities_sha256":canon([]),
"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":"initial governed supervisor runtime projection",
})
def resolver(_task_id,_job_id,_capability,_adapter,received_runtime_input):
assert received_runtime_input==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())
assert result["revision"]==2
assert result["attempt"]==0
assert result["idempotent"] is False
con=sqlite3.connect(path)
row=con.execute(
"SELECT status,attempt,revision,claimed_by,metadata_json,required_capabilities_json FROM tasks"
).fetchone()
assert row[:4]==("QUEUED",0,2,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]
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_INITIAL_EXECUTION_PROJECTION_AUTHORIZED"
]
payload=json.loads(events[-1][1])
assert payload["job_id"]==JOB_ID
assert payload["main_write"] is False
assert payload["production_write"] is False
def test_initial_projection_idempotent_exact_replay(tmp_path):
path=make_db(tmp_path)
store=InitialExecutionProjectionStore(path,resolver)
first=store.project(TASK_ID,request())
second=store.project(TASK_ID,request())
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_initial_execution_projections").fetchone()[0]==1
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",
[
({"claimed_by":"worker-a"},"TASK_ALREADY_CLAIMED"),
({"lease":True},"ACTIVE_LEASE_DENY"),
({"metadata":{"execution":{}}},"TASK_EXECUTION_ALREADY_PROJECTED"),
({"caps":["git.read"]},"TASK_INITIAL_CAPABILITIES_MUST_BE_EMPTY"),
({"event":"TASK_CLAIMED"},"TASK_INITIAL_EVENT_CHAIN_REQUIRED"),
],
)
def test_initial_projection_rejects_noninitial_state(tmp_path,kwargs,error):
path=make_db(tmp_path,**kwargs)
with pytest.raises(InitialExecutionProjectionError,match=error):
InitialExecutionProjectionStore(path,resolver).project(TASK_ID,request())
def test_initial_projection_rejects_binding_drift(tmp_path):
path=make_db(tmp_path)
def wrong(*_args):
return envelope(job_id="job-other")
with pytest.raises(InitialExecutionProjectionError,match="GOVERNED_BINDING_CONFLICT"):
InitialExecutionProjectionStore(path,wrong).project(TASK_ID,request())
def test_initial_projection_rejects_nonruntime_envelope(tmp_path):
path=make_db(tmp_path)
def wrong(*_args):
return envelope(action="source_capture",capability="git.read")
with pytest.raises(InitialExecutionProjectionError):
InitialExecutionProjectionStore(path,wrong).project(TASK_ID,request())
def test_initial_projection_rejects_execution_digest_drift(tmp_path):
path=make_db(tmp_path)
with pytest.raises(InitialExecutionProjectionError,match="EXECUTION_DIGEST_DRIFT"):
InitialExecutionProjectionStore(path,resolver).project(
TASK_ID,request(digest="f"*64)
)
def test_request_is_narrow_and_initial_state_bound():
data=request().public()
data["arbitrary_metadata"]={"x":1}
with pytest.raises(InitialExecutionProjectionError,match="FIELDS_INVALID"):
InitialExecutionProjectionRequest.from_mapping(data)
data=request().public();data["expected_attempt"]=1
with pytest.raises(InitialExecutionProjectionError,match="INITIAL_STATE_REQUIRED"):
InitialExecutionProjectionRequest.from_mapping(data)
def test_initial_projection_unit_is_unprivileged_and_owner_credential_bound():
unit=(ROOT/"deploy/systemd/aios-agent-task-initial-execution-projection.service").read_text()
assert "User=aios-agent-task" in unit
assert "Group=aios-agent-task" in unit
assert "LoadCredential=agent_task_control_token:" in unit
assert "Environment=AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892" in unit
assert "RestrictAddressFamilies=AF_UNIX AF_INET" in unit
assert "ReadWritePaths=/var/lib/aios-agent-task" in unit
assert "User=root" not in unit
@@ -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)
+252
View File
@@ -0,0 +1,252 @@
from __future__ import annotations
from datetime import datetime, timezone
import json
from pathlib import Path
import subprocess
import sys
import zipfile
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_all_modified_files_packager as packager
def init_repo(tmp_path: Path) -> tuple[Path, Path, str]:
bare=tmp_path/"repo.git"
work=tmp_path/"work"
subprocess.run(["git","init","--bare",str(bare)],check=True,capture_output=True)
subprocess.run(["git","clone",str(bare),str(work)],check=True,capture_output=True)
subprocess.run(["git","-C",str(work),"config","user.email","test@example.invalid"],check=True)
subprocess.run(["git","-C",str(work),"config","user.name","AIOS Test"],check=True)
(work/"README.md").write_text("base\n",encoding="utf-8")
subprocess.run(["git","-C",str(work),"add","README.md"],check=True)
subprocess.run(["git","-C",str(work),"commit","-m","base"],check=True,capture_output=True)
subprocess.run(["git","-C",str(work),"push","origin","HEAD:refs/heads/main"],check=True,capture_output=True)
base=subprocess.check_output(["git","-C",str(work),"rev-parse","HEAD"],text=True).strip()
return bare,work,base
def commit(work: Path, message: str) -> str:
subprocess.run(["git","-C",str(work),"add","-A"],check=True)
subprocess.run(["git","-C",str(work),"commit","-m",message],check=True,capture_output=True)
candidate=subprocess.check_output(["git","-C",str(work),"rev-parse","HEAD"],text=True).strip()
subprocess.run(["git","-C",str(work),"push","origin","HEAD:refs/heads/main"],check=True,capture_output=True)
return candidate
def real_pem_header() -> bytes:
return b"-----" + b"BEGIN " + b"PRIVATE KEY" + b"-----"
def bearer_value() -> bytes:
return b"Authorization" + b": Bearer " + (b"a"*40)
def key_value_secret() -> bytes:
return b"api_key" + b"=" + (b"x"*24)
def test_plain_private_key_words_are_not_secret_findings():
for value in (
b"BEGIN PRIVATE KEY",
b"PRIVATE KEY",
b"PEM private-key header",
b"assert 'BEGIN PRIVATE KEY' in message",
):
assert packager.secret_finding(value) is None
def test_complete_pem_private_key_header_is_rejected():
assert packager.secret_finding(real_pem_header())=="PEM_PRIVATE_KEY"
assert packager.secret_finding(
b"-----BEGIN OPENSSH " + b"PRIVATE KEY-----"
)=="PEM_PRIVATE_KEY"
def test_bearer_and_key_value_secret_detection_remains_fail_closed():
assert packager.secret_finding(bearer_value())=="AUTHORIZATION_BEARER"
assert packager.secret_finding(key_value_secret())=="KEY_VALUE_SECRET"
def test_short_or_descriptive_secret_words_do_not_false_positive():
assert packager.secret_finding(b"secret=short") is None
assert packager.secret_finding(b"password field is validated") is None
assert packager.secret_finding(b"api_key example placeholder") is None
def test_end_to_end_bundle_accepts_literal_begin_private_key_text(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"tests").mkdir()
(work/"tests"/"test_text.py").write_text(
'ERROR = "BEGIN PRIVATE KEY is not a complete PEM header"\n',
encoding="utf-8",
)
(work/"docs").mkdir()
(work/"docs"/"note.md").write_text("Scanner regression.\n",encoding="utf-8")
candidate=commit(work,"candidate")
extra=tmp_path/"execution"
extra.mkdir()
(extra/"evidence.json").write_text('{"status":"PASS"}\n',encoding="utf-8")
out=tmp_path/"out"
summary=packager.build_bundle(
git_dir=bare,
base=base,
candidate=candidate,
task_id="r42-packager-test",
extra_dir=extra,
output_root=out,
now=datetime(2026,8,19,tzinfo=timezone.utc),
)
assert summary["changed_file_count"]==2
assert summary["execution_file_count"]==1
assert summary["repository_document_count"]==1
assert summary["execution_document_count"]==0
bundle_path=Path(summary["bundle_path"])
assert bundle_path.is_file()
assert packager.sha256(bundle_path.read_bytes())==summary["bundle_sha256"]
with zipfile.ZipFile(bundle_path) as archive:
names=set(archive.namelist())
assert "repo/after/tests/test_text.py" in names
assert "repo/after/docs/note.md" in names
assert "execution/evidence.json" in names
assert "documents-index.json" in names
assert "all-modified-files-manifest.json" in names
assert "checksums.sha256" in names
manifest=json.loads(archive.read("all-modified-files-manifest.json"))
assert manifest["schema"]=="aios.all-modified-files.bundle.v1"
assert manifest["candidate_oid"]==candidate
packager.verify_checksums(out/"bundle")
def test_end_to_end_bundle_rejects_real_pem_header(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"credential.txt").write_bytes(
real_pem_header()+b"\nnot-a-real-key-body\n"
)
candidate=commit(work,"credential-like candidate")
with pytest.raises(
packager.PackagerError,
match="PEM_PRIVATE_KEY:after:credential.txt",
):
packager.build_bundle(
git_dir=bare,
base=base,
candidate=candidate,
task_id="r42-packager-pem-reject",
output_root=tmp_path/"out",
)
def test_execution_evidence_bearer_is_rejected(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"normal.txt").write_text("normal\n",encoding="utf-8")
candidate=commit(work,"normal")
extra=tmp_path/"execution"
extra.mkdir()
(extra/"unsafe.txt").write_bytes(bearer_value()+b"\n")
with pytest.raises(
packager.PackagerError,
match="AUTHORIZATION_BEARER:execution:unsafe.txt",
):
packager.build_bundle(
git_dir=bare,
base=base,
candidate=candidate,
task_id="r42-packager-bearer-reject",
extra_dir=extra,
output_root=tmp_path/"out",
)
def test_execution_symlink_is_rejected(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"normal.txt").write_text("normal\n",encoding="utf-8")
candidate=commit(work,"normal")
extra=tmp_path/"execution"
extra.mkdir()
target=extra/"target.txt"
target.write_text("safe\n",encoding="utf-8")
(extra/"link.txt").symlink_to(target)
with pytest.raises(
packager.PackagerError,
match="EXTRA_FILE_SYMLINK_REJECTED",
):
packager.build_bundle(
git_dir=bare,
base=base,
candidate=candidate,
task_id="r42-packager-symlink-reject",
extra_dir=extra,
output_root=tmp_path/"out",
)
def test_non_ancestor_lineage_is_rejected(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"one.txt").write_text("one\n",encoding="utf-8")
first=commit(work,"first")
subprocess.run(["git","-C",str(work),"checkout","--detach",base],check=True,capture_output=True)
(work/"other.txt").write_text("other\n",encoding="utf-8")
subprocess.run(["git","-C",str(work),"add","other.txt"],check=True)
subprocess.run(["git","-C",str(work),"commit","-m","other"],check=True,capture_output=True)
other=subprocess.check_output(["git","-C",str(work),"rev-parse","HEAD"],text=True).strip()
subprocess.run(
["git","-C",str(work),"push","origin","HEAD:refs/heads/other"],
check=True,
capture_output=True,
)
with pytest.raises(packager.PackagerError,match="LINEAGE_INVALID"):
packager.validate_lineage(bare,first,other)
def test_bundle_checksum_closure_detects_tamper(tmp_path):
bare,work,base=init_repo(tmp_path)
(work/"normal.txt").write_text("normal\n",encoding="utf-8")
candidate=commit(work,"normal")
out=tmp_path/"out"
packager.build_bundle(
git_dir=bare,
base=base,
candidate=candidate,
task_id="r42-packager-checksum",
output_root=out,
)
(out/"bundle"/"repo"/"after"/"normal.txt").write_text(
"tampered\n",
encoding="utf-8",
)
with pytest.raises(
packager.PackagerError,
match="CHECKSUM_CLOSURE_INVALID",
):
packager.verify_checksums(out/"bundle")
def test_source_does_not_contain_complete_pem_header_literal():
source=(ROOT/"active-runtime/app/aios_all_modified_files_packager.py").read_bytes()
tests=(ROOT/"tests/test_all_modified_files_packager.py").read_bytes()
complete=real_pem_header()
assert complete not in source
assert complete not in tests
def test_document_path_contract():
assert packager.is_document_path("docs/spec.md")
assert packager.is_document_path("README")
assert packager.is_document_path("CHANGELOG.md")
assert not packager.is_document_path("active-runtime/app/main.py")
+157
View File
@@ -0,0 +1,157 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_autonomous_control_plane as acp
import aios_capability_contract as contract
import aios_release_online_agent_worker as worker
def _ready_probes():
return {
"credential_projection": lambda: True,
"trusted_builder": lambda: True,
"authenticated_probe": lambda: True,
"release_api_health": lambda: True,
"worker_liveness": lambda: True,
}
def _task():
return {
"task_id":"r42-autonomous-release-admission-test",
"required_capabilities":["release:candidate"],
"constraints":{
"release_candidate":{
"candidate_id":"r42-release-candidate-v2r2c9-test",
"source_commit":"1"*40,
"version":"V2R2C9",
"projection_id":"projection-"+"a"*32,
}
},
}
def test_capability_contract_registry_is_closed_and_release_is_versioned():
status=contract.validate_capability_contract_registry()
assert status["valid"] is True
release=contract.capability_contract("release:candidate")
assert release is not None
assert release.min_version==1
assert release.max_version==1
assert release.permissions==("prepare_release",)
assert "credential_projection" in release.dependencies
assert "trusted_builder" in release.dependencies
assert "authenticated_probe" in release.health_requirements
assert "production_deploy" in release.forbidden
assert "runtime_switch" in release.forbidden
def test_unknown_agent_capability_cannot_register_or_match():
assert contract.registered_capability("release:future",worker=True) is False
assert contract.registered_capability("release:*",worker=True) is False
assert contract.has_capabilities(["release:future"],["release:future"]) is False
def test_runtime_admission_requires_all_release_execution_readiness():
decision=acp.runtime_admission(
worker_capabilities=["release:candidate"],
required_capabilities=["release:candidate"],
requested_operations=["prepare_release"],
probes=_ready_probes(),
)
assert decision.admitted is True
assert decision.state=="EXECUTION_READY"
def test_runtime_admission_blocks_credential_failure_before_execution():
probes=_ready_probes()
probes["credential_projection"]=lambda:False
probes["authenticated_probe"]=lambda:False
decision=acp.runtime_admission(
worker_capabilities=["release:candidate"],
required_capabilities=["release:candidate"],
requested_operations=["prepare_release"],
probes=probes,
)
assert decision.admitted is False
assert decision.state=="EXECUTION_READY_FAILED"
assert decision.failure_class==acp.FailureClass.CREDENTIAL_PROJECTION_ERROR
assert decision.repair_level==acp.RepairLevel.AUTO_REPAIR_TASK
def test_forbidden_release_operation_requires_owner_boundary():
decision=acp.runtime_admission(
worker_capabilities=["release:candidate"],
required_capabilities=["release:candidate"],
requested_operations=["production_deploy"],
probes=_ready_probes(),
)
assert decision.admitted is False
assert decision.failure_class==acp.FailureClass.POLICY_DENIED
assert decision.repair_level==acp.RepairLevel.OWNER_APPROVAL
@pytest.mark.parametrize(
"error,phase,expected,level,consumes",
[
("Permission denied: gitops.tokens","release",acp.FailureClass.CREDENTIAL_PROJECTION_ERROR,acp.RepairLevel.AUTO_REPAIR_TASK,False),
("HTTP_TRANSPORT_UNAVAILABLE","network",acp.FailureClass.DEPENDENCY_UNAVAILABLE,acp.RepairLevel.AUTO_LOCAL,False),
("TASK_CAPABILITY_SCOPE_INVALID","validation",acp.FailureClass.CAPABILITY_CONTRACT_ERROR,acp.RepairLevel.AUTO_REPAIR_TASK,False),
("lease lost","heartbeat",acp.FailureClass.LEASE_LOST,acp.RepairLevel.AUTO_LOCAL,False),
("business permanent invalid release","release",acp.FailureClass.BUSINESS_PERMANENT,acp.RepairLevel.MANUAL,True),
],
)
def test_failure_classification_drives_repair_policy(error,phase,expected,level,consumes):
failure=acp.classify_failure(error,phase=phase)
assert failure==expected
repair=acp.repair_for_failure(failure)
assert repair.level==level
assert repair.consumes_business_attempt is consumes
def test_release_worker_does_not_claim_when_admission_is_not_ready(monkeypatch):
calls=[]
def fake_http(base,method,route,token,body=None,timeout=30):
calls.append((base,method,route,body))
if route=="/health":
return 200,{"status":"ok"}
if route=="/createReleaseCandidate" and body=={}:
return 500,{"error":"Permission denied: gitops.tokens"}
if route.endswith("/claim"):
raise AssertionError("claim must not happen before EXECUTION_READY")
raise AssertionError(route)
events=[]
monkeypatch.setattr(worker,"http_json",fake_http)
monkeypatch.setattr(worker,"audit",lambda event,**fields:events.append((event,fields)))
monkeypatch.setattr(worker,"write_liveness",lambda *args,**kwargs:None)
assert worker.execute_task(
_task(),
agent_token="a"*32,
release_token="r"*32,
) is False
assert not any(route.endswith("/claim") for _,_,route,_ in calls)
blocked=[item for item in events if item[0]=="TASK_ADMISSION_BLOCKED"]
assert len(blocked)==1
assert blocked[0][1]["admission"]["state"]=="EXECUTION_READY_FAILED"
def test_system_invariant_snapshot_is_degraded_when_dependency_is_missing():
probes=_ready_probes()
probes["trusted_builder"]=lambda:False
snapshot=acp.system_invariant_snapshot(
worker_capabilities=["release:candidate"],
required_capabilities=["release:candidate"],
probes=probes,
)
assert snapshot["status"]=="DEGRADED"
assert snapshot["execution_ready"] is False
+627
View File
@@ -0,0 +1,627 @@
from __future__ import annotations
from datetime import datetime, timezone
import json
from pathlib import Path
import sqlite3
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_durable_owner_authorization as auth
NOW=datetime(2026,8,19,6,0,0,tzinfo=timezone.utc)
ACCOUNT="chatgpt-governed-implementer-singlehost"
def write_root_file(path: Path, value: object) -> Path:
path.parent.mkdir(parents=True,exist_ok=True)
path.write_text(json.dumps(value,sort_keys=True)+"\n",encoding="utf-8")
path.chmod(0o600)
return path
def policy_value(**overrides):
value={
"schema":auth.POLICY_SCHEMA,
"grant_id":"owner-permanent-preparation-20260819",
"owner_identity":"owner-singlehost",
"account":ACCOUNT,
"capabilities":[
"governed-task:project",
"release:candidate",
],
"issued_at":"2026-08-19T05:00:00Z",
"not_before":"2026-08-19T05:00:00Z",
"persistent_until_revoked":True,
"constraints":{
"repository":"aios/aios",
"allowed_target_prefix":"agent/",
"release_version_prefix":"V2R2C",
"max_task_risk":"engineering",
},
}
value.update(overrides)
return value
def load_policy(tmp_path,monkeypatch,**overrides):
path=write_root_file(tmp_path/"policy.json",policy_value(**overrides))
revocations=write_root_file(
tmp_path/"revocations.json",
{"revoked_grant_ids":[]},
)
monkeypatch.setattr(auth,"validate_root_file",lambda path,**kwargs:path)
return auth.DurablePolicy.load(
path,
revocations,
now=NOW,
),path,revocations
def governed_request(**overrides):
value={
"task_id":"r42-owner-auth-projection-test-20260819",
"repository":"aios/aios",
"base_oid":"8"*40,
"expected_branch_oid":"8"*40,
"target_branch":"agent/r42-owner-auth-projection-test-20260819",
"generation":1,
"risk":"engineering",
"description":"project existing governed task",
"idempotency_key":"binding-owner-auth-projection-v1",
"branch_mode":"CREATE_NEW",
"priority":100,
"max_attempts":3,
"require_online_task_projection":True,
"online_task":{
"goal":"Analyze source read-only.",
"instructions":"Use source capture first.",
"success_criteria":[
{"id":"source","description":"Return source snapshot."}
],
"constraints":{"repository_mutation":"forbidden"},
"required_capabilities":["git.read","git.test"],
"priority":100,
"max_attempts":3,
},
"execution":{
"action":"source_capture",
"adapter":"source_capture",
"capability_scope":["git.read","git.test"],
"source_signals":["durable authorization projection"],
},
}
value.update(overrides)
return value
def release_request(**overrides):
value={
"candidate_id":"r42-pf1-release-candidate-20260819",
"source_commit":"8"*40,
"version":"V2R2C2",
}
value.update(overrides)
return value
class FakeUpstream:
def __init__(self):
self.project_calls=[]
def project_governed_task(self,body):
self.project_calls.append(dict(body))
return 200,{
"task_id":body["task_id"],
"job_id":"job-"+"a"*32,
"state":"BOUND",
}
def broker(tmp_path,monkeypatch):
policy,path,revocations=load_policy(tmp_path,monkeypatch)
store=auth.ReceiptStore(tmp_path/"receipts.sqlite3")
upstream=FakeUpstream()
instance=auth.Broker(
policy_path=path,
revocation_path=revocations,
store=store,
upstream=upstream,
expected_account=ACCOUNT,
)
monkeypatch.setattr(
auth.DurablePolicy,
"load",
classmethod(lambda cls,*args,**kwargs:policy),
)
return instance,upstream,store,policy
def test_allowed_persistent_capabilities_are_only_preparation_scopes():
assert auth.ALLOWED_PERSISTENT_CAPABILITIES=={
"governed-task:project",
"release:candidate",
}
assert auth.ALLOWED_PERSISTENT_CAPABILITIES.isdisjoint(
auth.FORBIDDEN_PERSISTENT_CAPABILITIES
)
@pytest.mark.parametrize(
"capability",
sorted(auth.FORBIDDEN_PERSISTENT_CAPABILITIES),
)
def test_policy_rejects_high_risk_persistent_capability(
tmp_path,monkeypatch,capability
):
bad=policy_value(
capabilities=["governed-task:project",capability]
)
path=write_root_file(tmp_path/"policy.json",bad)
revocations=write_root_file(
tmp_path/"revocations.json",
{"revoked_grant_ids":[]},
)
monkeypatch.setattr(auth,"validate_root_file",lambda path,**kwargs:path)
with pytest.raises(
auth.AuthorizationError,
match="POLICY_CAPABILITY_NOT_PERSISTABLE",
):
auth.DurablePolicy.load(path,revocations,now=NOW)
def test_policy_is_persistent_until_revoked(tmp_path,monkeypatch):
policy,_,_=load_policy(tmp_path,monkeypatch)
assert policy.persistent_until_revoked is True
assert policy.account==ACCOUNT
assert policy.capabilities=={
"governed-task:project",
"release:candidate",
}
def test_revocation_fails_closed(tmp_path,monkeypatch):
path=write_root_file(tmp_path/"policy.json",policy_value())
revocations=write_root_file(
tmp_path/"revocations.json",
{"revoked_grant_ids":["owner-permanent-preparation-20260819"]},
)
monkeypatch.setattr(auth,"validate_root_file",lambda path,**kwargs:path)
with pytest.raises(auth.AuthorizationError,match="OWNER_GRANT_REVOKED"):
auth.DurablePolicy.load(path,revocations,now=NOW)
def test_future_policy_is_not_active(tmp_path,monkeypatch):
with pytest.raises(auth.AuthorizationError,match="POLICY_NOT_ACTIVE"):
load_policy(
tmp_path,
monkeypatch,
issued_at="2026-08-20T05:00:00Z",
not_before="2026-08-20T05:00:00Z",
)
def test_governed_projection_request_is_tightly_constrained(
tmp_path,monkeypatch
):
policy,_,_=load_policy(tmp_path,monkeypatch)
decision=policy.authorize(
"governed-task:project",
governed_request(),
)
assert decision["decision"]=="ALLOW"
assert decision["persistent_until_revoked"] is True
bad_caps=governed_request()
bad_caps["online_task"]={
**bad_caps["online_task"],
"required_capabilities":["release:candidate"],
}
bad_action=governed_request()
bad_action["execution"]={
**bad_action["execution"],
"action":"deterministic",
"adapter":"deterministic",
}
bad_scope=governed_request()
bad_scope["execution"]={
**bad_scope["execution"],
"capability_scope":["git.read"],
}
for bad in (
governed_request(repository="other/repo"),
governed_request(target_branch="main"),
governed_request(generation=2),
governed_request(risk="high"),
governed_request(expected_branch_oid="9"*40),
governed_request(branch_mode="CONTINUE_EXISTING"),
governed_request(priority=101),
governed_request(max_attempts=4),
governed_request(require_online_task_projection=False),
bad_caps,
bad_action,
bad_scope,
):
with pytest.raises(auth.AuthorizationError):
policy.authorize("governed-task:project",bad)
def test_write_capable_projection_is_allowed_only_as_source_capture_placeholder(
tmp_path,monkeypatch
):
policy,_,_=load_policy(tmp_path,monkeypatch)
request=governed_request()
request["online_task"]={
**request["online_task"],
"required_capabilities":[
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
],
}
request["execution"]={
**request["execution"],
"capability_scope":[
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
],
}
decision=policy.authorize("governed-task:project",request)
assert decision["decision"]=="ALLOW"
assert request["execution"]["action"]=="source_capture"
def test_release_candidate_request_is_tightly_constrained(
tmp_path,monkeypatch
):
policy,_,_=load_policy(tmp_path,monkeypatch)
decision=policy.authorize("release:candidate",release_request())
assert decision["decision"]=="ALLOW"
with pytest.raises(
auth.AuthorizationError,
match="RELEASE_VERSION_NOT_AUTHORIZED",
):
policy.authorize(
"release:candidate",
release_request(version="V3R1C1"),
)
with pytest.raises(
auth.AuthorizationError,
match="RELEASE_CANDIDATE_FIELDS_INVALID",
):
policy.authorize(
"release:candidate",
{**release_request(),"execute":True},
)
def test_governed_task_projection_executes_canonical_upstream(
tmp_path,monkeypatch
):
instance,upstream,store,_=broker(tmp_path,monkeypatch)
request=governed_request()
response=instance.execute(
"governed-task:project",
request,
idempotency_key="owner-auth-projection-operation-0001",
)
assert len(upstream.project_calls)==1
assert upstream.project_calls[0]==request
assert response["operation_state"]=="EXECUTED"
assert response["external_capability_projection_required"] is False
assert response["separate_owner_release_approval_still_required"] is True
def test_release_candidate_authorization_does_not_invent_builder_upstream(
tmp_path,monkeypatch
):
instance,upstream,store,_=broker(tmp_path,monkeypatch)
response=instance.execute(
"release:candidate",
release_request(),
idempotency_key="owner-auth-release-candidate-operation-0001",
)
assert upstream.project_calls==[]
assert response["operation_state"]==(
"AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION"
)
assert response["upstream_status"] is None
assert response["upstream_response"] is None
assert response["external_capability_projection_required"] is True
assert response["projection_id"].startswith("projection-")
assert response["projection_state"]=="PENDING_EXTERNAL_CONNECTOR"
assert response["separate_owner_release_approval_still_required"] is True
def test_release_authorization_creates_durable_projection_outbox(
tmp_path,monkeypatch
):
instance,_,store,policy=broker(tmp_path,monkeypatch)
request=release_request()
response=instance.execute(
"release:candidate",
request,
idempotency_key="owner-auth-release-outbox-0001",
)
projection=store.get_projection(response["projection_id"])
assert projection is not None
assert projection["schema"]==auth.PROJECTION_SCHEMA
assert projection["state"]=="PENDING_EXTERNAL_CONNECTOR"
assert projection["grant_id"]==policy.grant_id
assert projection["account"]==ACCOUNT
assert projection["capability"]=="release:candidate"
assert projection["request"]==request
assert projection["separate_owner_release_approval_still_required"] is True
listed=instance.projections()
assert listed["schema"]==auth.PROJECTION_LIST_SCHEMA
assert listed["count"]==1
assert listed["items"][0]["projection_id"]==response["projection_id"]
assert listed["external_connector_ack_supported"] is False
def test_existing_pre_outbox_release_receipt_is_backfilled_on_exact_retry(
tmp_path,monkeypatch
):
instance,_,store,policy=broker(tmp_path,monkeypatch)
request=release_request()
decision=policy.authorize("release:candidate",request)
key="owner-auth-release-pre-outbox-0001"
legacy_response={
"schema":auth.RECEIPT_SCHEMA,
"grant_id":policy.grant_id,
"owner_identity":policy.owner_identity,
"account":policy.account,
"capability":"release:candidate",
"policy_sha256":policy.policy_sha256,
"request_sha256":decision["request_sha256"],
"idempotency_key":key,
"operation_state":"AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION",
"upstream_status":None,
"upstream_response":None,
"external_capability_projection_required":True,
"separate_owner_release_approval_still_required":True,
}
store.put(
key,
"release:candidate",
decision["request_sha256"],
legacy_response,
)
response=instance.execute(
"release:candidate",
request,
idempotency_key=key,
)
assert response["projection_id"].startswith("projection-")
assert response["projection_state"]=="PENDING_EXTERNAL_CONNECTOR"
assert store.get_projection(response["projection_id"]) is not None
persisted=store.get(key)["response"]
assert persisted["projection_id"]==response["projection_id"]
def test_projection_id_is_deterministic_and_exact_retry_does_not_duplicate(
tmp_path,monkeypatch
):
instance,_,store,_=broker(tmp_path,monkeypatch)
key="owner-auth-release-outbox-0002"
first=instance.execute("release:candidate",release_request(),idempotency_key=key)
second=instance.execute("release:candidate",release_request(),idempotency_key=key)
assert second==first
con=sqlite3.connect(store.path)
rows=con.execute("select projection_id from projection_outbox").fetchall()
assert rows==[(first["projection_id"],)]
def test_projection_enqueue_recovers_crash_between_outbox_and_receipt(
tmp_path,monkeypatch
):
instance,_,store,policy=broker(tmp_path,monkeypatch)
request=release_request()
decision=policy.authorize("release:candidate",request)
key="owner-auth-release-outbox-crash-0001"
first=store.enqueue_projection(
idempotency_key=key,
grant_id=policy.grant_id,
account=policy.account,
capability="release:candidate",
policy_sha256=policy.policy_sha256,
request_sha256=decision["request_sha256"],
request=request,
)
# Simulate a later retry after the process crashed before receipt creation.
second=store.enqueue_projection(
idempotency_key=key,
grant_id=policy.grant_id,
account=policy.account,
capability="release:candidate",
policy_sha256=policy.policy_sha256,
request_sha256=decision["request_sha256"],
request=request,
)
assert second==first
def test_projection_idempotency_conflict_is_fail_closed(
tmp_path,monkeypatch
):
instance,_,store,policy=broker(tmp_path,monkeypatch)
first=release_request()
first_decision=policy.authorize("release:candidate",first)
key="owner-auth-release-outbox-conflict-0001"
store.enqueue_projection(
idempotency_key=key,
grant_id=policy.grant_id,
account=policy.account,
capability="release:candidate",
policy_sha256=policy.policy_sha256,
request_sha256=first_decision["request_sha256"],
request=first,
)
second=release_request(version="V2R2C3")
second_decision=policy.authorize("release:candidate",second)
with pytest.raises(auth.AuthorizationError,match="PROJECTION_IDEMPOTENCY_CONFLICT"):
store.enqueue_projection(
idempotency_key=key,
grant_id=policy.grant_id,
account=policy.account,
capability="release:candidate",
policy_sha256=policy.policy_sha256,
request_sha256=second_decision["request_sha256"],
request=second,
)
def test_revocation_blocks_projection_enumeration(tmp_path,monkeypatch):
instance,_,store,policy=broker(tmp_path,monkeypatch)
response=instance.execute(
"release:candidate",
release_request(),
idempotency_key="owner-auth-release-outbox-revoke-0001",
)
# Restore real policy loading and write a revocation for this grant.
monkeypatch.undo()
revocations=instance.revocation_path
revocations.write_text(
json.dumps({"revoked_grant_ids":[policy.grant_id]})+"\n",
encoding="utf-8",
)
revocations.chmod(0o600)
monkeypatch.setattr(auth,"validate_root_file",lambda path,**kwargs:path)
with pytest.raises(auth.AuthorizationError,match="OWNER_GRANT_REVOKED"):
instance.projections()
# Evidence is retained; revocation does not erase history.
assert store.get_projection(response["projection_id"]) is not None
def test_projection_outbox_contains_no_credentials(tmp_path,monkeypatch):
instance,_,store,_=broker(tmp_path,monkeypatch)
instance.execute(
"release:candidate",
release_request(),
idempotency_key="owner-auth-release-outbox-schema-0001",
)
con=sqlite3.connect(store.path)
columns=[row[1] for row in con.execute("pragma table_info(projection_outbox)")]
joined=" ".join(columns).lower()
for forbidden in ("token","credential","private_key","secret"):
assert forbidden not in joined
payload=con.execute("select payload_json from projection_outbox").fetchone()[0]
assert "Bearer " not in payload
assert "submitOwnerApproval" not in payload
assert "startDeployment" not in payload
def test_exact_retry_returns_same_receipt_without_second_projection(
tmp_path,monkeypatch
):
instance,upstream,_,_=broker(tmp_path,monkeypatch)
request=governed_request()
first=instance.execute(
"governed-task:project",
request,
idempotency_key="owner-auth-projection-operation-0002",
)
second=instance.execute(
"governed-task:project",
request,
idempotency_key="owner-auth-projection-operation-0002",
)
assert second==first
assert len(upstream.project_calls)==1
def test_idempotency_conflict_fails_closed(tmp_path,monkeypatch):
instance,_,_,_=broker(tmp_path,monkeypatch)
instance.execute(
"release:candidate",
release_request(),
idempotency_key="owner-auth-release-operation-0002",
)
with pytest.raises(auth.AuthorizationError,match="IDEMPOTENCY_CONFLICT"):
instance.execute(
"release:candidate",
release_request(version="V2R2C3"),
idempotency_key="owner-auth-release-operation-0002",
)
def test_receipt_store_contains_no_owner_policy_or_credentials(
tmp_path,monkeypatch
):
instance,_,store,_=broker(tmp_path,monkeypatch)
instance.execute(
"release:candidate",
release_request(),
idempotency_key="owner-auth-release-operation-0003",
)
con=sqlite3.connect(store.path)
columns=[row[1] for row in con.execute("pragma table_info(receipts)")]
joined=" ".join(columns).lower()
assert "token" not in joined
assert "credential" not in joined
assert "policy_json" not in columns
def test_systemd_broker_uses_unix_socket_and_only_host_control_credential():
unit=(
ROOT/"deploy/systemd/aios-owner-durable-authorization-broker.service"
).read_text()
assert "LoadCredential=agent_task_control_token:" in unit
assert "release_candidate_token" not in unit
assert "18947" not in unit
assert "AIOS_RELEASE_CANDIDATE_API_URL" not in unit
assert "AIOS_OWNER_AUTH_BROKER_SOCKET=%t/aios-owner-authorization-broker/broker.sock" in unit
assert "Group=aios-executor-exchange" in unit
assert "RestrictAddressFamilies=AF_UNIX AF_INET" in unit
assert "IPAddressDeny=any" in unit
assert "IPAddressAllow=127.0.0.0/8" in unit
assert "InaccessiblePaths=/data/aios-gitops /home" in unit
def test_source_contains_no_release_builder_endpoint_or_high_risk_execution():
source=(
ROOT/"active-runtime/app/aios_durable_owner_authorization.py"
).read_text()
assert "/createReleaseCandidate" not in source
assert "18947" not in source
assert "ThreadingHTTPServer" not in source
assert "ThreadingUnixStreamServer" in source
assert "startDeployment" in source
assert "tag_publish" in source
assert "baseline_registry_finalize" in source
assert "runtime_switch" in source
assert "AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION" in source
assert "PENDING_EXTERNAL_CONNECTOR" in source
assert "projection_outbox" in source
assert "external_connector_ack_supported" in source
assert "SAFE_PROJECTED_TASK_CAPABILITIES" in source
assert "INITIAL_EXECUTION_ACTION_NOT_AUTHORIZED" in source
def test_docs_avoid_new_broker_bearer_secret():
doc=(
ROOT/"docs/superpowers/specs/2026-08-19-durable-owner-authorization.md"
).read_text()
assert "AF_UNIX" in doc
assert "avoids introducing another long-lived bearer secret" in doc
def test_docs_preserve_separate_owner_release_approval():
doc=(
ROOT/"docs/superpowers/specs/2026-08-19-durable-owner-authorization.md"
).read_text()
assert "persistent until explicitly revoked" in doc.lower()
assert "Owner signature" in doc
assert "AUTHORIZED_PENDING_EXTERNAL_CAPABILITY_PROJECTION" in doc
assert "PENDING_EXTERNAL_CONNECTOR" in doc
@@ -0,0 +1,232 @@
from __future__ import annotations
import json
from pathlib import Path
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_online_agent_worker as worker
from aios_external_agent_runtime import (
ExternalAgentExchange,
ExternalAgentRuntimeError,
validate_request,
)
def envelope():
value=object.__new__(worker.ExecutionEnvelope)
object.__setattr__(value,"task_id","task-attempt-identity")
object.__setattr__(value,"job_id","job-attempt-identity")
object.__setattr__(value,"digest","d"*64)
return value
def test_external_request_id_is_stable_within_attempt_and_changes_across_attempts():
env=envelope()
a1=worker.external_agent_request_id(env,"hermes",6)
a1_replay=worker.external_agent_request_id(env,"hermes",6)
a2=worker.external_agent_request_id(env,"hermes",7)
assert a1==a1_replay
assert a1!=a2
assert a1.startswith("external-hermes-")
assert a2.startswith("external-hermes-")
def test_external_request_id_rejects_invalid_attempt():
env=envelope()
with pytest.raises(worker.WorkerError,match="TASK_ATTEMPT_INVALID"):
worker.external_agent_request_id(env,"hermes",0)
def test_external_request_schema_binds_attempt():
value=validate_request({
"schema":"aios.external-agent-runtime.request.v1",
"request_id":"external-hermes-0123456789abcdef",
"runtime":"hermes",
"task_id":"task-attempt-identity",
"attempt":7,
"execution_digest":"d"*64,
"payload":{
"goal":"Return ok.",
"instructions":"",
"success_criteria":[],
"constraints":{},
},
})
assert value["attempt"]==7
def test_external_request_schema_rejects_missing_attempt():
with pytest.raises(ExternalAgentRuntimeError,match="EXTERNAL_AGENT_ATTEMPT_INVALID"):
validate_request({
"schema":"aios.external-agent-runtime.request.v1",
"request_id":"external-hermes-0123456789abcdef",
"runtime":"hermes",
"task_id":"task-attempt-identity",
"execution_digest":"d"*64,
"payload":{
"goal":"Return ok.",
"instructions":"",
"success_criteria":[],
"constraints":{},
},
})
def _write_bound_result(path, request):
import hashlib
from aios_external_agent_runtime import canonical, sha256
material={
"schema":"aios.external-agent-runtime.result.v1",
"request_id":request["request_id"],
"request_sha256":sha256(canonical(request)),
"runtime":request["runtime"],
"task_id":request["task_id"],
"attempt":request["attempt"],
"execution_digest":request["execution_digest"],
"exit_code":0,
"duration_ms":1,
"output_text":"ok",
"output_sha256":hashlib.sha256(b"ok").hexdigest(),
"stderr_sha256":hashlib.sha256(b"").hexdigest(),
"credential_material_exposed":False,
"control_plane_access_granted":False,
}
material["result_sha256"]=sha256(canonical(material))
path.write_text(json.dumps(material,sort_keys=True),encoding="utf-8")
def test_exchange_result_binding_rejects_wrong_attempt(tmp_path):
exchange=ExternalAgentExchange(tmp_path)
request={
"schema":"aios.external-agent-runtime.request.v1",
"request_id":"external-hermes-0123456789abcdef",
"runtime":"hermes",
"task_id":"task-attempt-identity",
"attempt":7,
"execution_digest":"d"*64,
"payload":{
"goal":"Return ok.",
"instructions":"",
"success_criteria":[],
"constraints":{},
},
}
request=validate_request(request)
result_path=tmp_path/"result.json"
_write_bound_result(result_path,request)
value=json.loads(result_path.read_text())
value["attempt"]=6
# Recompute digest so the binding check, not the digest check, is what fails.
from aios_external_agent_runtime import canonical,sha256
material=dict(value); material.pop("result_sha256")
value["result_sha256"]=sha256(canonical(material))
result_path.write_text(json.dumps(value),encoding="utf-8")
with pytest.raises(ExternalAgentRuntimeError,match="EXTERNAL_AGENT_RESULT_BINDING_INVALID"):
exchange._validated_result(
result_path,
request,
sha256(canonical(request)),
)
def test_model_governance_request_id_is_attempt_scoped():
env=envelope()
attempt6=worker.external_agent_request_id(env,"hermes",6)+":model"
attempt7=worker.external_agent_request_id(env,"hermes",7)+":model"
assert attempt6!=attempt7
def test_failed_previous_attempt_does_not_poison_next_model_governance_reservation(tmp_path):
from aios_model_governance import (
ModelGovernanceStore,
ModelRecord,
ProviderRecord,
RouteRequest,
ScopePolicy,
)
store=ModelGovernanceStore(tmp_path/"model-governance.sqlite3",now_fn=lambda:1_800_000_000.0)
store.register_provider(
ProviderRecord(
"oneapi",
"runtime-profile:hermes",
max_failures=5,
cooldown_seconds=60,
)
)
store.register_model(
ModelRecord(
"deepseek-v4-flash",
"oneapi",
"v1",
("agent.runtime.hermes","text"),
input_cost_micros_per_1k=1,
output_cost_micros_per_1k=1,
max_input_tokens=10000,
max_output_tokens=1000,
evaluation_sha256="a"*64,
)
)
store.set_scope_policy(
ScopePolicy(
"tenant",
"model-governance-live",
"staging",
allowed_models=("deepseek-v4-flash",),
required_capabilities=("text",),
daily_token_budget=100000,
daily_cost_budget_micros=100000,
fallback_limit=1,
)
)
def route(request_id):
return store.route(
RouteRequest(
request_id=request_id,
task_id="r42-model-governance-hermes-live-20260818",
trace_id="trace-hermes-model-governance-live",
scope_type="tenant",
tenant_id="model-governance-live",
project_id="staging",
required_capabilities=("agent.runtime.hermes",),
estimated_input_tokens=100,
max_output_tokens=50,
)
)
attempt6=route("external-hermes-attempt6:model")
store.record_failure(attempt6.request_id,"provider_process_failed")
row6=store.conn.execute(
"select state from model_governance_reservations where request_id=?",
(attempt6.request_id,),
).fetchone()
assert row6["state"]=="FAILED"
attempt7=route("external-hermes-attempt7:model")
assert attempt7.request_id!=attempt6.request_id
row7=store.conn.execute(
"select state from model_governance_reservations where request_id=?",
(attempt7.request_id,),
).fetchone()
assert row7["state"]=="RESERVED"
settled=store.record_success(
attempt7.request_id,
input_tokens=90,
output_tokens=40,
usage_source="runtime",
)
assert settled["state"]=="SETTLED"
assert store.conn.execute(
"select state from model_governance_reservations where request_id=?",
(attempt6.request_id,),
).fetchone()["state"]=="FAILED"
assert store.verify_audit_chain() is True
store.close()
+4 -1
View File
@@ -110,6 +110,7 @@ def make_text_request(tmp_path, runtime, payload=None):
"request_id":f"external-{runtime}-0123456789abcdef",
"runtime":runtime,
"task_id":f"task-{runtime}-runtime",
"attempt":1,
"execution_digest":"d"*64,
"payload":payload or {
"goal":"Return ok.",
@@ -193,6 +194,7 @@ def test_external_executor_runs_root_owned_profile_without_shell(tmp_path,monkey
"request_id":f"external-{runtime}-0123456789abcdef",
"runtime":runtime,
"task_id":f"task-{runtime}-runtime",
"attempt":1,
"execution_digest":"d"*64,
"payload":{
"goal":"Return ok.",
@@ -326,6 +328,7 @@ def test_worker_external_adapter_builds_readonly_result(monkeypatch,tmp_path,run
"request_sha256":"e"*64,
"runtime":runtime,
"task_id":kwargs["task_id"],
"attempt":kwargs["attempt"],
"execution_digest":kwargs["execution_digest"],
"exit_code":0,
"duration_ms":10,
@@ -339,7 +342,7 @@ def test_worker_external_adapter_builds_readonly_result(monkeypatch,tmp_path,run
monkeypatch.setattr(worker,"ExternalAgentExchange",FakeExchange)
monkeypatch.setattr(worker,"audit",lambda *a,**k:None)
executor=worker.HermesExecutor() if runtime=="hermes" else worker.OpenClawExecutor()
execution=executor.execute(envelope,tmp_path)
execution=executor.execute(envelope,tmp_path,attempt=1)
result=worker.build_agent_runtime_result(envelope.task_id,envelope,execution)
assert result["status"]=="SUCCEEDED"
assert result["classification"]=="EXTERNAL_AGENT_RUNTIME_SUCCEEDED"
+205
View File
@@ -0,0 +1,205 @@
from __future__ import annotations
from email.message import Message
import json
from pathlib import Path
import stat
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_gpt_https_edge as edge
class Headers(dict):
def get(self,key,default=None):
return super().get(key,default)
def test_default_and_public_bind_require_explicit_owner_approval():
assert edge.public_bind_allowed("127.0.0.1","0")=="127.0.0.1"
assert edge.public_bind_allowed("::1","0")=="::1"
with pytest.raises(RuntimeError,match="OWNER_APPROVAL_REQUIRED"):
edge.public_bind_allowed("0.0.0.0","0")
with pytest.raises(RuntimeError,match="OWNER_APPROVAL_REQUIRED"):
edge.public_bind_allowed("::","")
assert edge.public_bind_allowed("0.0.0.0","1")=="0.0.0.0"
def test_arbitrary_public_bind_address_is_denied_even_with_approval():
with pytest.raises(RuntimeError,match="BIND_ADDRESS_INVALID"):
edge.public_bind_allowed("192.0.2.10","1")
def test_public_domain_validation():
assert edge.validated_public_domain("tasks.example.com")=="tasks.example.com"
assert edge.validated_public_domain("")==""
with pytest.raises(RuntimeError,match="PUBLIC_DOMAIN_INVALID"):
edge.validated_public_domain("https://example.com")
with pytest.raises(RuntimeError,match="PUBLIC_DOMAIN_INVALID"):
edge.validated_public_domain("localhost")
def test_route_allowlist_accepts_only_readonly_api():
assert edge.allowed_route("POST","/v1/read-only-tasks")=="/v1/read-only-tasks"
assert edge.allowed_route("GET","/v1/read-only-tasks/external-request-0001")==(
"/v1/read-only-tasks/external-request-0001"
)
assert edge.allowed_route("GET","/health")=="/health"
assert edge.allowed_route("GET","/openapi.json")=="/openapi.json"
for method,path in [
("POST","/v2/governed-task"),
("GET","/v1/tasks/x"),
("POST","/v1/read-only-tasks/x"),
("DELETE","/v1/read-only-tasks/x"),
]:
with pytest.raises(edge.EdgeError,match="ROUTE_DENY"):
edge.allowed_route(method,path)
def test_query_and_fragment_are_denied():
with pytest.raises(edge.EdgeError,match="QUERY_OR_FRAGMENT_DENY"):
edge.allowed_route("GET","/health?x=1")
with pytest.raises(edge.EdgeError,match="QUERY_OR_FRAGMENT_DENY"):
edge.allowed_route("GET","/openapi.json#x")
def test_forwarded_headers_are_minimal_and_require_bearer():
incoming=Headers({
"Authorization":"Bearer "+"a"*40,
"Content-Type":"application/json; charset=utf-8",
"Cookie":"secret=1",
"X-Forwarded-For":"203.0.113.10",
"X-Api-Key":"do-not-forward",
})
forwarded=edge.forwarded_headers(incoming,body=True)
assert forwarded=={
"Accept":"application/json",
"Authorization":"Bearer "+"a"*40,
"Content-Type":"application/json",
}
with pytest.raises(edge.EdgeError,match="AUTHORIZATION_REQUIRED"):
edge.forwarded_headers(Headers({}),body=False)
def test_invalid_content_type_is_denied():
with pytest.raises(edge.EdgeError,match="CONTENT_TYPE_INVALID"):
edge.forwarded_headers(
Headers({
"Authorization":"Bearer "+"a"*40,
"Content-Type":"text/plain",
}),
body=True,
)
def test_rate_limiter_caps_window_and_allows_expiry():
now=[100.0]
limiter=edge.RateLimiter(window_seconds=60,limit=2,burst=1,now_fn=lambda:now[0])
assert limiter.allowed("client")
assert limiter.allowed("client")
assert limiter.allowed("client")
assert not limiter.allowed("client")
now[0]=161.0
assert limiter.allowed("client")
def test_rate_limiter_client_table_is_bounded():
now=[100.0]
limiter=edge.RateLimiter(
window_seconds=60,
limit=10,
burst=0,
max_clients=2,
now_fn=lambda:now[0],
)
assert limiter.allowed("a")
assert limiter.allowed("b")
assert not limiter.allowed("c")
now[0]=161.0
assert limiter.allowed("c")
assert len(limiter._events) <= 2
def test_audit_source_does_not_log_sensitive_request_material():
source=(ROOT/"active-runtime/app/aios_gpt_https_edge.py").read_text()
assert '"authorization", "body", "token", "cookie", "client_ip"' in source
assert "_client_pseudonym" in source
def test_proxy_upstream_is_fixed():
proxy=edge.Proxy()
assert proxy.upstream=="http://127.0.0.1:18945"
bad=edge.Proxy(upstream="http://127.0.0.1:18892")
with pytest.raises(edge.EdgeError,match="UPSTREAM_DRIFT_DENY"):
bad.request("GET","/health",{"Authorization":"Bearer "+"a"*40},None)
def test_public_bind_requires_approved_domain():
assert edge.validated_edge_identity("127.0.0.1","0","")==("127.0.0.1","")
with pytest.raises(RuntimeError,match="PUBLIC_DOMAIN_REQUIRED"):
edge.validated_edge_identity("0.0.0.0","1","")
assert edge.validated_edge_identity(
"0.0.0.0","1","tasks.example.com"
)==("0.0.0.0","tasks.example.com")
def test_tls_key_requires_no_group_or_other_permissions(tmp_path):
key=tmp_path/"key.pem"
key.write_text("x"*64,encoding="utf-8")
key.chmod(0o600)
assert edge.validate_tls_file(key,key=True)==key
key.chmod(0o644)
with pytest.raises(RuntimeError,match="TLS_KEY_MODE_INVALID"):
edge.validate_tls_file(key,key=True)
def test_tls_symlink_is_denied(tmp_path):
real=tmp_path/"cert.pem"
real.write_text("x"*64,encoding="utf-8")
link=tmp_path/"link.pem"
link.symlink_to(real)
with pytest.raises(RuntimeError,match="TLS_FILE_INVALID"):
edge.validate_tls_file(link,key=False)
def test_systemd_unit_is_disabled_by_owner_approval_condition_and_loopback_default():
unit=(ROOT/"deploy/systemd/aios-gpt-https-edge.service").read_text()
assert "ConditionPathExists=/etc/aios-gitops/gpt-https-edge.approved" in unit
assert "AIOS_GPT_EDGE_HOST=127.0.0.1" in unit
assert "AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=0" in unit
assert "AIOS_GPT_EDGE_PORT=18443" in unit
assert "LoadCredential=tls_cert:" in unit
assert "LoadCredential=tls_key:" in unit
assert "InaccessiblePaths=/data/aios-gitops" in unit
assert "InaccessiblePaths=" in unit and "/var/lib/aios-agent-task" in unit
assert "NoNewPrivileges=yes" in unit
assert "ProtectSystem=strict" in unit
def test_systemd_unit_does_not_reference_internal_control_credentials():
unit=(ROOT/"deploy/systemd/aios-gpt-https-edge.service").read_text()
assert "agent_task_control" not in unit
assert "gitea" not in unit.lower()
assert "18892" not in unit
assert "18940" not in unit
def test_edge_source_does_not_proxy_internal_bridge_or_task_api():
source=(ROOT/"active-runtime/app/aios_gpt_https_edge.py").read_text()
assert 'UPSTREAM = "http://127.0.0.1:18945"' in source
assert "18892" not in source
assert "18940" not in source
assert "/v2/governed-task" not in source
assert "/v1/tasks/" not in source
def test_docs_keep_public_activation_separately_approved():
doc=(ROOT/"docs/superpowers/specs/2026-08-19-gpt-https-edge.md").read_text()
assert "explicit owner approval" in doc.lower()
assert "PUBLIC_NETWORK_EXPOSURE=NONE" in doc
assert "PUBLIC_CUSTOM_GPT_DIRECT_AI0S_ACCESS=NOT_VERIFIED" in doc
+362
View File
@@ -0,0 +1,362 @@
from __future__ import annotations
from datetime import datetime, timedelta, timezone
import hashlib
import json
from pathlib import Path
import sys
import subprocess
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_gpt_https_edge_activation as activation
CANDIDATE="69a51d50c7f6cd9045346d41e338acebe4b6f1e8"
NOW=datetime(2026,8,19,2,0,0,tzinfo=timezone.utc)
def approval_value(cert_sha: str, **overrides):
value={
"schema":"aios.gpt-https-edge.public-approval.v1",
"approval_id":"owner-edge-approval-0001",
"candidate_oid":CANDIDATE,
"domain":"tasks.example.com",
"public_bind":True,
"bind_host":"0.0.0.0",
"port":18443,
"tls_cert_sha256":cert_sha,
"issued_at":"2026-08-19T01:00:00Z",
"not_before":"2026-08-19T01:00:00Z",
"expires_at":"2026-08-20T01:00:00Z",
"owner":"owner@example",
}
value.update(overrides)
return value
def write_owner_file(path: Path, data: bytes, mode=0o600):
path.write_bytes(data)
path.chmod(mode)
return path
def load_approval(tmp_path, monkeypatch, **overrides):
tmp_path.mkdir(parents=True,exist_ok=True)
cert=write_owner_file(tmp_path/"cert.pem",b"certificate-material-"*4,0o600)
value=approval_value(hashlib.sha256(cert.read_bytes()).hexdigest(),**overrides)
approval=write_owner_file(
tmp_path/"approval.json",
(json.dumps(value,sort_keys=True)+"\n").encode(),
0o600,
)
monkeypatch.setattr(
activation,
"validate_root_file",
lambda path, **kwargs: path,
)
return activation.Approval.load(
approval,
candidate_oid=CANDIDATE,
cert_path=cert,
now=NOW,
),approval,cert
class FakeSystemd:
def __init__(self):
self.active_units={activation.INGRESS_UNIT}
self.enabled_units=set()
self.calls=[]
self.exec_start=""
self.environment=""
self.fail_restart=False
def run(self,*args,check=True):
self.calls.append(args)
class Result:
returncode=0
stdout=""
if args[:2]==("restart",activation.EDGE_UNIT):
if self.fail_restart:
raise subprocess.CalledProcessError(1,args)
self.active_units.add(activation.EDGE_UNIT)
elif args[:2]==("stop",activation.EDGE_UNIT):
self.active_units.discard(activation.EDGE_UNIT)
elif args[:2]==("enable",activation.EDGE_UNIT):
self.enabled_units.add(activation.EDGE_UNIT)
elif args[:2]==("disable",activation.EDGE_UNIT):
self.enabled_units.discard(activation.EDGE_UNIT)
return Result()
def active(self,unit):
return unit in self.active_units
def enabled(self,unit):
return unit in self.enabled_units
def show(self,unit,prop):
if prop=="ExecStart":
return self.exec_start
if prop=="Environment":
return self.environment
return ""
def test_approval_binds_candidate_domain_bind_port_and_cert(tmp_path,monkeypatch):
approval,_,cert=load_approval(tmp_path,monkeypatch)
assert approval.candidate_oid==CANDIDATE
assert approval.domain=="tasks.example.com"
assert approval.bind_host=="0.0.0.0"
assert approval.port==18443
assert approval.tls_cert_sha256==hashlib.sha256(cert.read_bytes()).hexdigest()
assert approval.expires_at>approval.not_before
def test_approval_rejects_candidate_mismatch(tmp_path,monkeypatch):
cert=write_owner_file(tmp_path/"cert.pem",b"certificate-material-"*4)
value=approval_value(hashlib.sha256(cert.read_bytes()).hexdigest())
approval=write_owner_file(tmp_path/"approval.json",json.dumps(value).encode())
monkeypatch.setattr(activation,"validate_root_file",lambda path,**kwargs:path)
with pytest.raises(activation.ActivationError,match="CANDIDATE_MISMATCH"):
activation.Approval.load(
approval,
candidate_oid="0"*40,
cert_path=cert,
now=NOW,
)
def test_approval_rejects_cert_hash_mismatch(tmp_path,monkeypatch):
cert=write_owner_file(tmp_path/"cert.pem",b"certificate-material-"*4)
value=approval_value("a"*64)
approval=write_owner_file(tmp_path/"approval.json",json.dumps(value).encode())
monkeypatch.setattr(activation,"validate_root_file",lambda path,**kwargs:path)
with pytest.raises(activation.ActivationError,match="CERT_SHA_MISMATCH"):
activation.Approval.load(
approval,
candidate_oid=CANDIDATE,
cert_path=cert,
now=NOW,
)
def test_approval_rejects_expired_not_yet_valid_and_long_lived(tmp_path,monkeypatch):
with pytest.raises(activation.ActivationError,match="APPROVAL_EXPIRED"):
load_approval(
tmp_path/"expired",
monkeypatch,
issued_at="2026-08-17T01:00:00Z",
not_before="2026-08-17T01:00:00Z",
expires_at="2026-08-18T01:00:00Z",
)
# Use distinct temp roots because helper writes fixed names.
with pytest.raises(activation.ActivationError,match="NOT_YET_VALID"):
load_approval(
tmp_path/"future",
monkeypatch,
issued_at="2026-08-19T03:00:00Z",
not_before="2026-08-19T03:00:00Z",
expires_at="2026-08-20T03:00:00Z",
)
with pytest.raises(activation.ActivationError,match="VALIDITY_TOO_LONG"):
load_approval(
tmp_path/"long",
monkeypatch,
issued_at="2026-08-18T01:00:00Z",
not_before="2026-08-18T01:00:00Z",
expires_at="2026-08-26T01:00:00Z",
)
def test_validate_release_requires_edge_and_controller_and_denies_symlink(tmp_path):
release=tmp_path/"release"
app=release/"active-runtime/app"
app.mkdir(parents=True)
edge_file=app/"aios_gpt_https_edge.py"
controller=app/"aios_gpt_https_edge_activation.py"
edge_file.write_text("edge\n")
controller.write_text("controller\n")
value=activation.validate_release(release)
assert value["edge"]==edge_file
assert value["controller"]==controller
controller.unlink()
with pytest.raises(activation.ActivationError,match="IMMUTABLE_RELEASE_INVALID"):
activation.validate_release(release)
def test_render_dropin_is_exact_and_contains_no_secret(tmp_path,monkeypatch):
approval,_,_=load_approval(tmp_path,monkeypatch)
release=Path("/opt/aios/releases")/(CANDIDATE+"-gpt-https-edge-public")
text=activation.render_dropin(approval,release)
assert "AIOS_GPT_EDGE_HOST=0.0.0.0" in text
assert "AIOS_GPT_EDGE_PORT=18443" in text
assert "AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=1" in text
assert "AIOS_GPT_EDGE_PUBLIC_DOMAIN=tasks.example.com" in text
assert CANDIDATE in text
for forbidden in ("Bearer ","agent_task_control","tls_key","gpt_ingress_token"):
assert forbidden not in text
def test_receipt_contains_binding_but_no_owner_secret_material(tmp_path,monkeypatch):
approval,_,_=load_approval(tmp_path,monkeypatch)
receipt=activation.receipt_for(
approval,
Path("/opt/aios/releases/test"),
state="ACTIVE",
)
assert receipt["approval_id"]==approval.approval_id
assert receipt["candidate_oid"]==CANDIDATE
assert receipt["domain"]=="tasks.example.com"
serialized=json.dumps(receipt)
assert "Bearer " not in serialized
assert "private" not in serialized.lower()
def test_activate_is_idempotent_for_same_active_approval(tmp_path,monkeypatch):
approval,_,cert=load_approval(tmp_path,monkeypatch)
release=tmp_path/"release"
app=release/"active-runtime/app/aios_gpt_https_edge.py"
controller=release/"active-runtime/app/aios_gpt_https_edge_activation.py"
app.parent.mkdir(parents=True)
app.write_text("print('x')\n")
controller.write_text("print('controller')\n")
key=write_owner_file(tmp_path/"key.pem",b"private-key-material-"*4,0o600)
dropin=tmp_path/"edge.d"/activation.DROPIN_NAME
state=tmp_path/"state.json"
systemd=FakeSystemd()
monkeypatch.setattr(activation.os,"geteuid",lambda:0)
monkeypatch.setattr(activation,"require_ingress_ready",lambda _:None)
monkeypatch.setattr(activation,"validate_tls_pair",lambda *a,**k:{"cert_sha256":approval.tls_cert_sha256})
monkeypatch.setattr(activation,"tls_probe",lambda *a,**k:None)
systemd.exec_start=str(app)
systemd.environment=(
"AIOS_GPT_EDGE_HOST=0.0.0.0 "
"AIOS_GPT_EDGE_PORT=18443 "
"AIOS_GPT_EDGE_PUBLIC_BIND_APPROVED=1 "
"AIOS_GPT_EDGE_PUBLIC_DOMAIN=tasks.example.com"
)
first=activation.activate(
approval=approval,
release_root=release,
cert_path=cert,
key_path=key,
dropin_path=dropin,
state_path=state,
systemd=systemd,
)
calls_after_first=len(systemd.calls)
second=activation.activate(
approval=approval,
release_root=release,
cert_path=cert,
key_path=key,
dropin_path=dropin,
state_path=state,
systemd=systemd,
)
assert first["state"]=="ACTIVE"
assert second==json.loads(state.read_text())
assert len(systemd.calls)==calls_after_first
def test_activate_failure_after_mutation_rolls_back(tmp_path,monkeypatch):
approval,_,cert=load_approval(tmp_path,monkeypatch)
release=tmp_path/"release"
app=release/"active-runtime/app/aios_gpt_https_edge.py"
controller=release/"active-runtime/app/aios_gpt_https_edge_activation.py"
app.parent.mkdir(parents=True)
app.write_text("print('x')\n")
controller.write_text("print('controller')\n")
key=write_owner_file(tmp_path/"key.pem",b"private-key-material-"*4,0o600)
dropin=tmp_path/"edge.d"/activation.DROPIN_NAME
state=tmp_path/"state.json"
systemd=FakeSystemd()
systemd.fail_restart=True
monkeypatch.setattr(activation.os,"geteuid",lambda:0)
monkeypatch.setattr(activation,"require_ingress_ready",lambda _:None)
monkeypatch.setattr(activation,"validate_tls_pair",lambda *a,**k:{"cert_sha256":approval.tls_cert_sha256})
monkeypatch.setattr(activation,"tls_probe",lambda *a,**k:None)
with pytest.raises(subprocess.CalledProcessError):
activation.activate(
approval=approval,
release_root=release,
cert_path=cert,
key_path=key,
dropin_path=dropin,
state_path=state,
systemd=systemd,
)
assert not dropin.exists()
assert not systemd.active(activation.EDGE_UNIT)
assert json.loads(state.read_text())["state"]=="ROLLED_BACK"
def test_rollback_is_replay_safe_and_leaves_owner_assets_alone(tmp_path,monkeypatch):
approval_file=tmp_path/"approval.json"
cert=tmp_path/"cert.pem"
key=tmp_path/"key.pem"
for path in (approval_file,cert,key):
path.write_text("owner-asset"*4)
dropin=tmp_path/"edge.d"/activation.DROPIN_NAME
dropin.parent.mkdir(parents=True)
dropin.write_text("[Service]\n")
state=tmp_path/"state.json"
state.write_text(json.dumps({
"schema":activation.STATE_SCHEMA,
"state":"ACTIVE",
"candidate_oid":CANDIDATE,
"approval_id":"owner-edge-approval-0001",
"domain":"tasks.example.com",
}))
systemd=FakeSystemd()
systemd.active_units.add(activation.EDGE_UNIT)
systemd.enabled_units.add(activation.EDGE_UNIT)
monkeypatch.setattr(activation.os,"geteuid",lambda:0)
first=activation.rollback(
dropin_path=dropin,
state_path=state,
systemd=systemd,
reason="owner_requested",
)
second=activation.rollback(
dropin_path=dropin,
state_path=state,
systemd=systemd,
reason="owner_requested",
)
assert first["state"]=="ROLLED_BACK"
assert second["state"]=="ROLLED_BACK"
assert not dropin.exists()
assert not systemd.active(activation.EDGE_UNIT)
assert not systemd.enabled(activation.EDGE_UNIT)
for path in (approval_file,cert,key):
assert path.exists()
def test_activation_source_never_creates_owner_approval_or_tls_secret():
source=(ROOT/"active-runtime/app/aios_gpt_https_edge_activation.py").read_text()
assert "secrets.token" not in source
assert "openssl" not in source.lower()
assert "BEGIN PRIVATE KEY" not in source
assert "gpt-https-edge.approved" in source
assert "gpt-https-edge-tls.key" in source
def test_docs_separate_implementation_activation_and_runtime_verification():
doc=(ROOT/"docs/superpowers/specs/2026-08-19-gpt-https-edge-activation.md").read_text()
assert "IMPLEMENTED" in doc
assert "OWNER_APPROVED" in doc
assert "ACTIVATED" in doc
assert "RUNTIME_VERIFIED" in doc
assert "does not own" in doc.lower()
+342
View File
@@ -0,0 +1,342 @@
from __future__ import annotations
import json
from pathlib import Path
import sqlite3
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
from aios_gpt_online_task_ingress import (
IngressError,
IngressService,
Store,
READONLY_CAPABILITIES,
branch_for,
normalized_request,
openapi_document,
task_id_for,
validated_bind_host,
)
MAIN="3244f0f17f8845592ac94d585f5ebd65693c9f29"
JOB="job-0123456789abcdef0123456789abcdef"
def request(request_id="external-request-0001", **overrides):
value={
"schema":"aios.gpt-online-task-ingress.request.v1",
"request_id":request_id,
"goal":"Inspect the repository for durable worker state.",
"instructions":"Read only. Return a concise source analysis.",
"success_criteria":[
{"id":"analysis","description":"Return a source-backed analysis."}
],
"constraints":{
"max_files":50,
"focus_paths":["active-runtime/app","tests"],
"notes":"Do not mutate repository state.",
},
}
value.update(overrides)
return value
class FakeUpstream:
def __init__(self):
self.created=[]
self.projected=[]
self.tasks={}
self.snapshot={"authoritative_head_oid":MAIN}
def control_snapshot(self):
return dict(self.snapshot)
def create_governed_task(self, body):
self.created.append(dict(body))
task_id=body["task_id"]
job_id=JOB
self.tasks[task_id]={
"task_id":task_id,
"status":"PROJECTED",
"attempt":0,
"metadata":{
"execution":{
"task_id":task_id,
"job_id":job_id,
"base_oid":body["base_oid"],
"action":"source_capture",
"capability_scope":list(READONLY_CAPABILITIES),
"governance":{"writer_lease":False},
}
},
}
return 201,{"task_id":task_id,"job_id":job_id,"state":"BOUND"}
def execution_envelope(self, body):
self.projected.append(dict(body))
task_id=body["task_id"]
return 200,{
"schema":"aios.online-agent.execution.v2",
"task_id":task_id,
"job_id":body["job_id"],
"repository":"aios/aios",
"base_oid":MAIN,
"action":"source_capture",
"capability_scope":list(READONLY_CAPABILITIES),
"governance":{"writer_lease":False},
}
def get_task(self, task_id):
value=self.tasks.get(task_id)
return (404,{}) if value is None else (200,dict(value))
def service(tmp_path):
upstream=FakeUpstream()
store=Store(tmp_path/"state.sqlite3")
return IngressService(upstream,store),upstream,store
def test_request_schema_is_narrow_and_rejects_capability_or_execution_control():
value=normalized_request(request())
assert value["goal"].startswith("Inspect")
for field in ("capability_scope","required_capabilities","action","adapter","repository","base_oid"):
bad=request()
bad[field]="evil"
with pytest.raises(IngressError,match="UNKNOWN_FIELDS"):
normalized_request(bad)
def test_constraints_are_bounded_and_path_traversal_is_denied():
bad=request(constraints={"focus_paths":["../etc/passwd"]})
with pytest.raises(IngressError,match="FOCUS_PATHS_INVALID"):
normalized_request(bad)
bad=request(constraints={"max_files":1000})
with pytest.raises(IngressError,match="MAX_FILES_INVALID"):
normalized_request(bad)
def test_non_loopback_bind_is_denied():
assert validated_bind_host("127.0.0.1")=="127.0.0.1"
with pytest.raises(RuntimeError,match="GPT_INGRESS_NON_LOOPBACK_BIND_DENY"):
validated_bind_host("0.0.0.0")
with pytest.raises(RuntimeError,match="GPT_INGRESS_NON_LOOPBACK_BIND_DENY"):
validated_bind_host("192.0.2.10")
def test_task_and_branch_ids_are_deterministic_and_bounded():
a=task_id_for("external-request-0001")
b=task_id_for("external-request-0001")
c=task_id_for("external-request-0002")
assert a==b
assert a!=c
assert a.startswith("gpt-readonly-")
branch=branch_for(a)
assert branch.startswith("agent/external-gpt-readonly/")
assert len(a)<180
def test_create_translates_to_canonical_readonly_governed_task(tmp_path):
svc,upstream,store=service(tmp_path)
response=svc.create(request())
assert response["state"]=="BOUND"
assert response["write_access"] is False
assert response["official_verification_required"] is True
assert response["capabilities"]==list(READONLY_CAPABILITIES)
assert len(upstream.created)==1
body=upstream.created[0]
assert body["repository"]=="aios/aios"
assert body["base_oid"]==MAIN
assert body["expected_branch_oid"]==MAIN
assert body["branch_mode"]=="CREATE_NEW"
assert body["risk"]=="low"
assert body["priority"]==0
assert body["max_attempts"]==3
assert body["require_online_task_projection"] is True
assert body["online_task"]["required_capabilities"]==list(READONLY_CAPABILITIES)
assert body["online_task"]["constraints"]["repository_mutation"]=="forbidden"
assert body["online_task"]["constraints"]["credential_access"]=="forbidden"
assert body["execution"]["action"]=="source_capture"
assert body["execution"]["adapter"]=="source_capture"
assert body["execution"]["capability_scope"]==list(READONLY_CAPABILITIES)
assert "writer_lease" not in body["execution"]
assert len(upstream.projected)==1
projection=upstream.projected[0]
assert projection["task_id"]==response["task_id"]
assert projection["job_id"]==JOB
assert projection["required_capabilities"]==list(READONLY_CAPABILITIES)
row=store.get("external-request-0001")
assert row is not None
assert row["state"]=="BOUND"
assert row["job_id"]==JOB
assert row["base_oid"]==MAIN
def test_exact_retry_is_idempotent_without_duplicate_bridge_call(tmp_path):
svc,upstream,_=service(tmp_path)
first=svc.create(request())
second=svc.create(request())
assert second==first
assert len(upstream.created)==1
assert len(upstream.projected)==1
def test_same_request_id_with_different_content_is_conflict(tmp_path):
svc,upstream,_=service(tmp_path)
svc.create(request())
with pytest.raises(IngressError,match="IDEMPOTENCY_CONFLICT"):
svc.create(request(goal="Different goal."))
assert len(upstream.created)==1
def test_pending_request_recovers_existing_canonical_task_after_crash(tmp_path):
svc,upstream,store=service(tmp_path)
raw=normalized_request(request())
task_id=task_id_for(raw["request_id"])
store.reserve(raw,task_id)
upstream.tasks[task_id]={
"task_id":task_id,
"status":"PROJECTED",
"attempt":0,
"metadata":{
"execution":{
"task_id":task_id,
"job_id":JOB,
"base_oid":MAIN,
"action":"source_capture",
"capability_scope":list(READONLY_CAPABILITIES),
"governance":{"writer_lease":False},
}
},
}
result=svc.create(request())
assert result["job_id"]==JOB
assert result["task_id"]==task_id
assert len(upstream.created)==0
row=store.get(raw["request_id"])
assert row["state"]=="BOUND"
def test_status_is_sanitized_and_never_proxies_raw_execution_or_result_text(tmp_path):
svc,upstream,store=service(tmp_path)
created=svc.create(request())
task_id=created["task_id"]
upstream.tasks[task_id]={
"task_id":task_id,
"status":"SUCCEEDED",
"attempt":1,
"claimed_by":"online-engineering-worker-cn-network-mkt-generic-20260815",
"metadata":{
"execution":{
"job_id":JOB,
"base_oid":MAIN,
"action":"source_capture",
"capability_scope":list(READONLY_CAPABILITIES),
"governance":{"writer_lease":False},
"secret_marker":"do-not-return",
}
},
"result":{
"status":"SUCCEEDED",
"classification":"NEEDS_REWORK_SOURCE_CAPTURE",
"execution_mode":"source_capture",
"output_text":"raw source text that must not be proxied",
"source_snapshot":{
"payload_sha256":"a"*64,
"file_count":42,
"files":["secret/path"],
},
},
"verification":{
"decision":"SUCCEEDED",
"classification":"GENERIC_ONLINE_TASK_AUTO_CONSUMPTION_VERIFIED",
"private":"do-not-return",
},
}
status=svc.status("external-request-0001")
assert status["state"]=="SUCCEEDED"
assert status["attempt"]==1
assert status["claimed"] is True
assert status["official_verification"]=="SUCCEEDED"
assert status["source_snapshot_sha256"]=="a"*64
assert status["source_snapshot_file_count"]==42
serialized=json.dumps(status)
assert "raw source text" not in serialized
assert "secret_marker" not in serialized
assert "secret/path" not in serialized
assert "do-not-return" not in serialized
def test_pending_status_can_recover_job_binding(tmp_path):
svc,upstream,store=service(tmp_path)
raw=normalized_request(request())
task_id=task_id_for(raw["request_id"])
store.reserve(raw,task_id)
upstream.tasks[task_id]={
"task_id":task_id,
"status":"PROJECTED",
"attempt":0,
"metadata":{
"execution":{
"job_id":JOB,
"base_oid":MAIN,
"action":"source_capture",
"capability_scope":list(READONLY_CAPABILITIES),
"governance":{"writer_lease":False},
}
},
}
value=svc.status(raw["request_id"])
assert value["job_id"]==JOB
assert store.get(raw["request_id"])["state"]=="BOUND"
def test_openapi_exposes_only_create_readonly_and_status_operations():
doc=openapi_document()
assert doc["openapi"]=="3.1.0"
paths=doc["paths"]
assert set(paths)=={
"/v1/read-only-tasks",
"/v1/read-only-tasks/{request_id}",
}
rendered=json.dumps(doc)
for forbidden in ("writer-lease","commit","verify","production","tag","baseline"):
assert forbidden not in rendered.lower()
def test_systemd_unit_is_loopback_credential_isolated_and_has_no_repository_access():
unit=(ROOT/"deploy/systemd/aios-gpt-online-task-ingress.service").read_text()
assert "DynamicUser=yes" in unit
assert "LoadCredential=gpt_ingress_token:" in unit
assert "LoadCredential=agent_task_control_token:" in unit
assert "AIOS_GPT_INGRESS_HOST=127.0.0.1" in unit
assert "AIOS_GOVERNED_WRITE_API_URL=http://127.0.0.1:18892" in unit
assert "AIOS_AGENT_TASK_API_URL=http://127.0.0.1:18940" in unit
assert "IPAddressDeny=any" in unit
assert "IPAddressAllow=127.0.0.0/8" in unit
assert "InaccessiblePaths=/data/aios-gitops /etc/aios-gitops /home" in unit
assert "ProtectSystem=strict" in unit
assert "NoNewPrivileges=yes" in unit
assert "MemoryDenyWriteExecute=yes" in unit
assert "0.0.0.0" not in unit
def test_ingress_sqlite_contains_no_request_body_or_credentials(tmp_path):
svc,upstream,store=service(tmp_path)
svc.create(request(instructions="sensitive-looking but non-secret user text"))
con=sqlite3.connect(store.path)
columns=[row[1] for row in con.execute("pragma table_info(external_task_requests)")]
assert "request_json" not in columns
assert "goal" not in columns
assert "instructions" not in columns
assert "token" not in " ".join(columns).lower()
@@ -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)
+325
View File
@@ -0,0 +1,325 @@
from __future__ import annotations
from pathlib import Path
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_model_governance import (
APPROVED,
ModelGovernanceError,
ModelGovernanceStore,
ModelRecord,
ProviderRecord,
RouteRequest,
ScopePolicy,
)
def setup_store(tmp_path, now=1_800_000_000.0):
store=ModelGovernanceStore(tmp_path/"model-governance.sqlite3",now_fn=lambda:now)
store.register_provider(
ProviderRecord(
"provider-a",
"runtime-profile:provider-a",
max_failures=1,
cooldown_seconds=120,
)
)
store.register_provider(
ProviderRecord(
"provider-b",
"runtime-profile:provider-b",
max_failures=2,
cooldown_seconds=60,
)
)
store.register_model(
ModelRecord(
"model-a",
"provider-a",
"2026-08-18",
("agent.runtime.hermes","text"),
input_cost_micros_per_1k=100,
output_cost_micros_per_1k=200,
max_input_tokens=10000,
max_output_tokens=4000,
evaluation_state=APPROVED,
evaluation_sha256="a"*64,
priority=10,
)
)
store.register_model(
ModelRecord(
"model-b",
"provider-b",
"2026-08-17",
("agent.runtime.hermes","text"),
input_cost_micros_per_1k=120,
output_cost_micros_per_1k=250,
max_input_tokens=10000,
max_output_tokens=4000,
evaluation_state=APPROVED,
evaluation_sha256="b"*64,
priority=20,
)
)
store.set_scope_policy(
ScopePolicy(
"tenant",
"tenant-a",
"project-a",
allowed_models=("model-a","model-b"),
required_capabilities=("text",),
max_input_tokens_per_request=5000,
max_output_tokens_per_request=2000,
daily_token_budget=12000,
daily_cost_budget_micros=4000,
fallback_limit=2,
)
)
return store
def req(request_id="request-001", *, exclude=()):
return RouteRequest(
request_id=request_id,
task_id="task-a",
trace_id="trace-a",
scope_type="tenant",
tenant_id="tenant-a",
project_id="project-a",
required_capabilities=("agent.runtime.hermes",),
estimated_input_tokens=1000,
max_output_tokens=500,
preferred_models=(),
exclude_providers=exclude,
)
def test_registry_rejects_raw_secret(tmp_path):
store=ModelGovernanceStore(tmp_path/"db.sqlite3")
with pytest.raises(ModelGovernanceError,match="MODEL_CREDENTIAL"):
store.register_provider(
ProviderRecord("provider-a","Bearer raw-secret-value")
)
def test_model_registration_requires_evaluation_gate(tmp_path):
store=ModelGovernanceStore(tmp_path/"db.sqlite3")
store.register_provider(ProviderRecord("provider-a"))
with pytest.raises(ModelGovernanceError,match="MODEL_EVALUATION_GATE_DENY"):
store.register_model(
ModelRecord(
"model-a","provider-a","v1",("text",),
evaluation_state="PENDING",evaluation_sha256="a"*64
)
)
def test_scope_policy_requires_version_with_pin(tmp_path):
store=ModelGovernanceStore(tmp_path/"db.sqlite3")
with pytest.raises(ModelGovernanceError,match="MODEL_POLICY_PIN_VERSION_REQUIRED"):
store.set_scope_policy(
ScopePolicy("tenant","t","p",pinned_model_id="model-a")
)
def test_route_is_deterministic_version_pinned_and_idempotent(tmp_path):
store=setup_store(tmp_path)
first=store.route(req())
second=store.route(req())
assert first==second
assert first.model_id=="model-a"
assert first.model_version=="2026-08-18"
assert first.provider_credential_ref=="runtime-profile:provider-a"
assert first.policy_revision==1
assert first.model_revision==1
snapshot=store.usage_snapshot("tenant","tenant-a","project-a")
assert snapshot["reserved_input_tokens"]==1000
assert snapshot["reserved_output_tokens"]==500
assert snapshot["reserved_cost_micros"]>0
def test_route_idempotency_payload_conflict_fails(tmp_path):
store=setup_store(tmp_path)
store.route(req())
changed=RouteRequest(
**{**req().__dict__,"estimated_input_tokens":2000}
)
with pytest.raises(ModelGovernanceError,match="MODEL_ROUTE_IDEMPOTENCY_CONFLICT"):
store.route(changed)
def test_budget_reservation_is_atomic(tmp_path):
store=setup_store(tmp_path)
store.set_scope_policy(
ScopePolicy(
"tenant","tenant-a","project-a",
allowed_models=("model-a",),
required_capabilities=("text",),
daily_token_budget=1600,
daily_cost_budget_micros=10000,
),
expected_revision=1,
)
store.route(req("request-1"))
with pytest.raises(ModelGovernanceError,match="MODEL_BUDGET_DENY"):
store.route(req("request-2"))
def test_usage_settlement_is_idempotent_and_releases_reservation(tmp_path):
store=setup_store(tmp_path)
decision=store.route(req())
first=store.record_success(
decision.request_id,
input_tokens=900,
output_tokens=400,
usage_source="runtime",
)
second=store.record_success(
decision.request_id,
input_tokens=900,
output_tokens=400,
usage_source="runtime",
)
assert first["idempotent"] is False
assert second["idempotent"] is True
snapshot=store.usage_snapshot("tenant","tenant-a","project-a")
assert snapshot["settled_input_tokens"]==900
assert snapshot["settled_output_tokens"]==400
assert snapshot["reserved_input_tokens"]==0
assert snapshot["reserved_output_tokens"]==0
def test_missing_runtime_usage_falls_back_to_reserved_upper_bound(tmp_path):
store=setup_store(tmp_path)
decision=store.route(req())
result=store.record_success(
decision.request_id,
input_tokens=None,
output_tokens=None,
)
assert result["usage_source"]=="reserved_upper_bound"
assert result["input_tokens"]==1000
assert result["output_tokens"]==500
def test_usage_cannot_exceed_reserved_budget(tmp_path):
store=setup_store(tmp_path)
decision=store.route(req())
with pytest.raises(ModelGovernanceError,match="MODEL_USAGE_EXCEEDS_RESERVATION"):
store.record_success(
decision.request_id,
input_tokens=1001,
output_tokens=500,
)
def test_provider_failure_opens_circuit_and_fallback_uses_other_provider(tmp_path):
store=setup_store(tmp_path)
original=req()
first=store.route(original)
assert first.provider_id=="provider-a"
failure=store.record_failure(first.request_id,"provider_timeout")
assert failure["open_until"]>0
fallback=store.fallback(original,first.request_id)
assert fallback.provider_id=="provider-b"
assert fallback.fallback_index==1
def test_circuit_excludes_provider_for_new_request(tmp_path):
store=setup_store(tmp_path)
first=store.route(req("request-first"))
store.record_failure(first.request_id,"provider_timeout")
next_decision=store.route(req("request-next"))
assert next_decision.provider_id=="provider-b"
def test_model_version_pin_is_enforced(tmp_path):
store=setup_store(tmp_path)
store.set_scope_policy(
ScopePolicy(
"tenant","tenant-a","project-a",
allowed_models=("model-a","model-b"),
pinned_model_id="model-b",
pinned_model_version="2026-08-17",
daily_token_budget=12000,
daily_cost_budget_micros=4000,
),
expected_revision=1,
)
decision=store.route(req())
assert decision.model_id=="model-b"
assert decision.model_version=="2026-08-17"
def test_scope_isolation_denies_missing_cross_scope_policy(tmp_path):
store=setup_store(tmp_path)
cross=RouteRequest(
request_id="request-cross",
task_id="task-a",
trace_id="trace-a",
scope_type="tenant",
tenant_id="tenant-b",
project_id="project-a",
required_capabilities=("agent.runtime.hermes",),
estimated_input_tokens=100,
max_output_tokens=50,
)
with pytest.raises(ModelGovernanceError,match="MODEL_SCOPE_POLICY_MISSING"):
store.route(cross)
def test_audit_chain_detects_tampering(tmp_path):
store=setup_store(tmp_path)
decision=store.route(req())
store.record_success(decision.request_id,input_tokens=100,output_tokens=50)
assert store.verify_audit_chain() is True
store.conn.execute(
"UPDATE model_governance_audit SET payload_json='{}' WHERE sequence=1"
)
assert store.verify_audit_chain() is False
def test_registry_and_policy_use_cas(tmp_path):
store=ModelGovernanceStore(tmp_path/"db.sqlite3")
store.register_provider(ProviderRecord("p"))
with pytest.raises(ModelGovernanceError,match="MODEL_PROVIDER_CAS_MISMATCH"):
store.register_provider(ProviderRecord("p"),expected_revision=7)
store.set_scope_policy(ScopePolicy("tenant","t","p"))
with pytest.raises(ModelGovernanceError,match="MODEL_POLICY_CAS_MISMATCH"):
store.set_scope_policy(
ScopePolicy("tenant","t","p"),
expected_revision=7,
)
def test_system_policy_never_authorizes_missing_tenant_policy(tmp_path):
store=ModelGovernanceStore(tmp_path/"db.sqlite3")
store.register_provider(ProviderRecord("provider-a"))
store.register_model(
ModelRecord(
"model-a","provider-a","v1",("text",),
evaluation_sha256="a"*64,
)
)
store.set_scope_policy(
ScopePolicy("system",None,None,allowed_models=("model-a",))
)
request=RouteRequest(
request_id="request-tenant-no-policy",
task_id="task-a",
trace_id="trace-a",
scope_type="tenant",
tenant_id="tenant-a",
project_id="project-a",
required_capabilities=("text",),
estimated_input_tokens=10,
max_output_tokens=10,
)
with pytest.raises(ModelGovernanceError,match="MODEL_SCOPE_POLICY_MISSING"):
store.route(request)
+82
View File
@@ -0,0 +1,82 @@
from __future__ import annotations
import json
from pathlib import Path
import sys
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_model_governance_admin import apply_config
from aios_model_governance import ModelGovernanceStore
def config():
return {
"schema":"aios.model-governance.bootstrap.v1",
"providers":[{
"provider_id":"provider-a",
"credential_ref":"runtime-profile:provider-a",
"max_failures":2,
"cooldown_seconds":60,
}],
"models":[{
"model_id":"model-a",
"provider_id":"provider-a",
"version":"v1",
"capabilities":["text"],
"input_cost_micros_per_1k":1,
"output_cost_micros_per_1k":2,
"max_input_tokens":1000,
"max_output_tokens":100,
"evaluation_state":"APPROVED",
"evaluation_sha256":"a"*64,
"priority":10,
}],
"policies":[{
"scope_type":"legacy",
"tenant_id":None,
"project_id":None,
"allowed_models":["model-a"],
"required_capabilities":["text"],
"daily_token_budget":10000,
"daily_cost_budget_micros":1000,
"fallback_limit":0,
}],
}
def test_admin_apply_is_idempotent(tmp_path):
cfg=tmp_path/"config.json"
cfg.write_text(json.dumps(config()),encoding="utf-8")
db=tmp_path/"db.sqlite3"
first=apply_config(db,cfg)
second=apply_config(db,cfg)
assert first["changes"]["providers"]==1
assert first["changes"]["models"]==1
assert first["changes"]["policies"]==1
assert second["changes"]["providers"]==0
assert second["changes"]["models"]==0
assert second["changes"]["policies"]==0
assert second["changes"]["unchanged"]==3
store=ModelGovernanceStore(db)
assert store.verify_audit_chain() is True
store.close()
def test_admin_config_change_uses_cas_revision(tmp_path):
value=config()
cfg=tmp_path/"config.json"
cfg.write_text(json.dumps(value),encoding="utf-8")
db=tmp_path/"db.sqlite3"
apply_config(db,cfg)
value["providers"][0]["cooldown_seconds"]=120
cfg.write_text(json.dumps(value),encoding="utf-8")
result=apply_config(db,cfg)
assert result["changes"]["providers"]==1
store=ModelGovernanceStore(db)
row=store.conn.execute(
"select revision,cooldown_seconds from model_governance_providers where provider_id='provider-a'"
).fetchone()
assert tuple(row)==(2,120)
store.close()
@@ -0,0 +1,554 @@
from __future__ import annotations
import json
import os
from pathlib import Path
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
from aios_model_governance import (
ModelGovernanceStore,
ModelRecord,
ProviderRecord,
ScopePolicy,
)
from aios_online_agent_execution import GovernedExecutionBinding, project_online_task
import aios_external_agent_runtime as runtime
BASE="a"*40
def binding(task_id):
return GovernedExecutionBinding.from_mapping({
"task_id":task_id,
"job_id":"job-"+task_id,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":"agent/"+task_id,
"branch_mode":"CREATE_NEW",
"generation":1,
"idempotency_key":task_id+"-binding-v1",
})
def projected(task_id="task-model-governed", *, scope_type="tenant", tenant_id="tenant-a", project_id="project-a"):
request={
"task_id":task_id,
"goal":"Return the requested sentinel.",
"instructions":"No repository or control-plane side effects.",
"success_criteria":[{"id":"done","description":"Return a result."}],
"constraints":{"repository_mutation":"forbidden"},
"scope_type":scope_type,
"tenant_id":tenant_id,
"project_id":project_id,
"trace_id":"trace-model-a",
"required_capabilities":["agent.runtime.hermes"],
}
return project_online_task(
binding(task_id),
request,
{
"action":"agent_runtime",
"adapter":"hermes",
"capability_scope":["agent.runtime.hermes"],
},
)
def helper(tmp_path):
path=tmp_path/"model_runtime.py"
path.write_text(
"import json,sys\n"
"model=sys.argv[1]\n"
"provider=sys.argv[2]\n"
"payload=json.load(sys.stdin)\n"
"if model=='model-a' and payload.get('constraints',{}).get('force_fail_a'):\n"
" raise SystemExit(7)\n"
"print(json.dumps({'answer':'ok','model':model,'provider':provider,"
"'usage':{'input_tokens':20,'output_tokens':10,'cost_micros':2}},sort_keys=True))\n",
encoding="utf-8",
)
return path
def write_profile(tmp_path, helper_path, *, providers=("provider-a","provider-b")):
root=tmp_path/"profiles";root.mkdir(exist_ok=True)
value={
"schema":"aios.external-agent-runtime.profile.v1",
"runtime":"hermes",
"argv":["/usr/bin/python3",str(helper_path),"{model_id}","{provider_id}"],
"input_mode":"stdin_json",
"output_mode":"stdout_json",
"timeout_seconds":30,
"max_output_bytes":65536,
"environment":{},
"credential_env":{},
"model_governance":{
"required":True,
"provider_ids":list(providers),
"model_capabilities":["text"],
"max_output_tokens":100,
},
}
(root/"hermes.json").write_text(json.dumps(value),encoding="utf-8")
return root
def seed(db_path, *, legacy=False, tiny_budget=False):
store=ModelGovernanceStore(db_path,now_fn=lambda:1_800_000_000.0)
for provider in ("provider-a","provider-b"):
store.register_provider(
ProviderRecord(
provider,
f"runtime-profile:{provider}",
max_failures=1 if provider=="provider-a" else 2,
cooldown_seconds=120,
)
)
store.register_model(
ModelRecord(
"model-a","provider-a","v1",
("agent.runtime.hermes","text"),
input_cost_micros_per_1k=10,
output_cost_micros_per_1k=20,
max_input_tokens=5000,max_output_tokens=1000,
evaluation_sha256="a"*64,priority=1,
)
)
store.register_model(
ModelRecord(
"model-b","provider-b","v2",
("agent.runtime.hermes","text"),
input_cost_micros_per_1k=12,
output_cost_micros_per_1k=22,
max_input_tokens=5000,max_output_tokens=1000,
evaluation_sha256="b"*64,priority=2,
)
)
if legacy:
policy=ScopePolicy(
"legacy",None,None,
allowed_models=("model-a","model-b"),
required_capabilities=("text",),
daily_token_budget=10000,
daily_cost_budget_micros=10000,
fallback_limit=2,
)
else:
policy=ScopePolicy(
"tenant","tenant-a","project-a",
allowed_models=("model-a","model-b"),
required_capabilities=("text",),
daily_token_budget=1 if tiny_budget else 10000,
daily_cost_budget_micros=10000,
fallback_limit=2,
)
store.set_scope_policy(policy)
store.close()
def make_external_request(projected_task, *, force_fail=False, attempt=1):
execution=projected_task["metadata"]["execution"]
payload=dict(execution["runtime_input"])
if force_fail:
payload["constraints"]=dict(payload["constraints"])
payload["constraints"]["force_fail_a"]=True
return {
"schema":"aios.external-agent-runtime.request.v1",
"request_id":"external-model-governance-0123456789",
"runtime":"hermes",
"task_id":projected_task["task_id"],
"attempt":attempt,
"execution_digest":execution["execution_digest"],
"payload":payload,
}
@pytest.fixture(autouse=True)
def clean_env(monkeypatch):
for key in list(os.environ):
upper=key.upper()
if any(marker in upper for marker in ("TOKEN","SECRET","PASSWORD","CREDENTIAL","AUTHORIZATION","PRIVATE_KEY","API_KEY")):
monkeypatch.delenv(key,raising=False)
monkeypatch.setenv("CREDENTIALS_DIRECTORY","/definitely-not-present-model-governance")
def test_execution_projection_binds_trusted_scope_and_trace():
task=projected()
runtime_input=task["metadata"]["execution"]["runtime_input"]
assert runtime_input["scope"]=={
"scope_type":"tenant",
"tenant_id":"tenant-a",
"project_id":"project-a",
}
assert runtime_input["trace_id"]=="trace-model-a"
def test_legacy_scope_remains_explicit_not_fake_tenant():
task=projected(scope_type="legacy",tenant_id=None,project_id=None)
scope=task["metadata"]["execution"]["runtime_input"]["scope"]
assert scope=={"scope_type":"legacy","tenant_id":None,"project_id":None}
def test_profile_requires_model_binding_when_governance_required(tmp_path):
path=tmp_path/"helper.py";path.write_text("print('ok')\n")
root=tmp_path/"profiles";root.mkdir()
(root/"hermes.json").write_text(json.dumps({
"schema":"aios.external-agent-runtime.profile.v1",
"runtime":"hermes",
"argv":["/usr/bin/python3",str(path)],
"input_mode":"stdin_text",
"output_mode":"stdout_text",
"model_governance":{"required":True,"provider_ids":["provider-a"],"max_output_tokens":100},
}))
with pytest.raises(runtime.ExternalAgentRuntimeError,match="EXTERNAL_AGENT_MODEL_BINDING_REQUIRED"):
runtime.RuntimeProfile.load("hermes",root)
def test_governed_runtime_selects_model_and_settles_usage(tmp_path,monkeypatch):
helper_path=helper(tmp_path)
profile_root=write_profile(tmp_path,helper_path)
db=tmp_path/"governance.sqlite3";seed(db)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
request_path=tmp_path/"request.json"
result_path=tmp_path/"result.json"
request_path.write_text(json.dumps(make_external_request(projected())),encoding="utf-8")
runtime.execute_request(request_path,result_path,profile_root=profile_root)
result=json.loads(result_path.read_text())
mg=result["model_governance"]
assert mg["model_id"]=="model-a"
assert mg["model_version"]=="v1"
assert mg["provider_id"]=="provider-a"
assert mg["settlement"]["usage_source"]=="runtime"
assert mg["settlement"]["input_tokens"]==20
assert mg["settlement"]["output_tokens"]==10
assert mg["audit_chain_valid"] is True
assert result["output_json"]["model"]=="model-a"
def test_budget_denies_before_runtime_process(tmp_path,monkeypatch):
helper_path=helper(tmp_path)
profile_root=write_profile(tmp_path,helper_path)
db=tmp_path/"governance.sqlite3";seed(db,tiny_budget=True)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
req=tmp_path/"request.json";res=tmp_path/"result.json"
req.write_text(json.dumps(make_external_request(projected())),encoding="utf-8")
with pytest.raises(runtime.ExternalAgentRuntimeError,match="MODEL_BUDGET_DENY"):
runtime.execute_request(req,res,profile_root=profile_root)
assert not res.exists()
def test_provider_failure_routes_to_fallback_provider(tmp_path,monkeypatch):
helper_path=helper(tmp_path)
profile_root=write_profile(tmp_path,helper_path)
db=tmp_path/"governance.sqlite3";seed(db)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
req=tmp_path/"request.json";res=tmp_path/"result.json"
req.write_text(json.dumps(make_external_request(projected(),force_fail=True)),encoding="utf-8")
runtime.execute_request(req,res,profile_root=profile_root)
result=json.loads(res.read_text())
mg=result["model_governance"]
assert len(mg["attempts"])==2
assert mg["attempts"][0]["provider_id"]=="provider-a"
assert mg["attempts"][1]["provider_id"]=="provider-b"
assert mg["provider_id"]=="provider-b"
assert result["output_json"]["provider"]=="provider-b"
def test_profile_provider_allowlist_fails_closed(tmp_path,monkeypatch):
helper_path=helper(tmp_path)
profile_root=write_profile(tmp_path,helper_path,providers=("provider-b",))
db=tmp_path/"governance.sqlite3";seed(db)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
req=tmp_path/"request.json";res=tmp_path/"result.json"
req.write_text(json.dumps(make_external_request(projected())),encoding="utf-8")
with pytest.raises(runtime.ExternalAgentRuntimeError,match="EXTERNAL_AGENT_MODEL_PROVIDER_PROFILE_DENY"):
runtime.execute_request(req,res,profile_root=profile_root)
def test_governance_required_but_runtime_disabled_fails_closed(tmp_path,monkeypatch):
helper_path=helper(tmp_path)
profile_root=write_profile(tmp_path,helper_path)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",False)
req=tmp_path/"request.json";res=tmp_path/"result.json"
req.write_text(json.dumps(make_external_request(projected())),encoding="utf-8")
with pytest.raises(runtime.ExternalAgentRuntimeError,match="EXTERNAL_AGENT_MODEL_GOVERNANCE_DISABLED"):
runtime.execute_request(req,res,profile_root=profile_root)
def test_execution_request_cannot_override_authoritative_task_scope():
task_id="task-scope-override"
request={
"task_id":task_id,
"goal":"Return result.",
"instructions":"",
"success_criteria":[],
"constraints":{},
"scope_type":"tenant",
"tenant_id":"tenant-a",
"project_id":"project-a",
"trace_id":"trace-authoritative",
"required_capabilities":["agent.runtime.hermes"],
}
task=project_online_task(
binding(task_id),
request,
{
"action":"agent_runtime",
"adapter":"hermes",
"capability_scope":["agent.runtime.hermes"],
"runtime_input":{
"goal":"Return result.",
"instructions":"",
"success_criteria":[],
"constraints":{},
"scope":{
"scope_type":"tenant",
"tenant_id":"tenant-b",
"project_id":"project-b",
},
"trace_id":"trace-forged",
},
},
)
runtime_input=task["metadata"]["execution"]["runtime_input"]
assert runtime_input["scope"]["tenant_id"]=="tenant-a"
assert runtime_input["scope"]["project_id"]=="project-a"
assert runtime_input["trace_id"]=="trace-authoritative"
def test_openclaw_nested_usage_is_extracted_exactly():
value={
"meta":{
"agentMeta":{
"usage":{"input":7770,"output":13,"total":7783},
"lastCallUsage":{"input":7770,"output":13,"total":7783},
"costUsd":0,
}
}
}
assert runtime._extract_runtime_usage(value)==(7770,13,0)
def test_governance_required_profile_needs_provider_allowlist_and_output_limit(tmp_path):
path=tmp_path/"helper.py";path.write_text("print('ok')\\n")
root=tmp_path/"profiles";root.mkdir()
(root/"hermes.json").write_text(json.dumps({
"schema":"aios.external-agent-runtime.profile.v1",
"runtime":"hermes",
"argv":["/usr/bin/python3",str(path),"{model_id}"],
"input_mode":"stdin_text",
"output_mode":"stdout_text",
"model_governance":{"required":True},
}))
with pytest.raises(
runtime.ExternalAgentRuntimeError,
match="EXTERNAL_AGENT_MODEL_PROVIDER_IDS_REQUIRED",
):
runtime.RuntimeProfile.load("hermes",root)
def _openclaw_context_helper(tmp_path, *, input_tokens=7770, output_tokens=13):
path=tmp_path/"openclaw_context_helper.py"
path.write_text(
"import json,sys\n"
"model=sys.argv[1]\n"
"provider=sys.argv[2]\n"
"json.load(sys.stdin)\n"
f"print(json.dumps({{'answer':'ok','model':model,'provider':provider,"
f"'meta':{{'agentMeta':{{'usage':{{'input':{input_tokens},'output':{output_tokens},"
f"'total':{input_tokens+output_tokens}}},'costUsd':0}}}}}},sort_keys=True))\n",
encoding="utf-8",
)
return path
def _openclaw_floor_profile(tmp_path, helper_path, *, floor=16384):
root=tmp_path/"openclaw-profiles"
root.mkdir(exist_ok=True)
value={
"schema":"aios.external-agent-runtime.profile.v1",
"runtime":"openclaw",
"argv":["/usr/bin/python3",str(helper_path),"{model_id}","{provider_id}"],
"input_mode":"stdin_json",
"output_mode":"stdout_json",
"timeout_seconds":30,
"max_output_bytes":65536,
"environment":{},
"credential_env":{},
"model_governance":{
"required":True,
"provider_ids":["openai"],
"model_capabilities":["text"],
"max_output_tokens":256,
"input_reservation_floor_tokens":floor,
},
}
(root/"openclaw.json").write_text(json.dumps(value),encoding="utf-8")
return root
def _seed_openclaw_floor_db(db_path):
store=ModelGovernanceStore(db_path,now_fn=lambda:1_800_000_000.0)
store.register_provider(
ProviderRecord(
"openai",
"runtime-profile:openclaw",
max_failures=2,
cooldown_seconds=60,
)
)
store.register_model(
ModelRecord(
"openai/deepseek-v4-flash",
"openai",
"v1",
("agent.runtime.openclaw","text"),
input_cost_micros_per_1k=0,
output_cost_micros_per_1k=0,
max_input_tokens=32768,
max_output_tokens=256,
evaluation_sha256="c"*64,
priority=10,
)
)
store.set_scope_policy(
ScopePolicy(
"tenant",
"model-governance-live",
"staging",
allowed_models=("openai/deepseek-v4-flash",),
required_capabilities=("text",),
max_input_tokens_per_request=32768,
max_output_tokens_per_request=256,
daily_token_budget=250000,
daily_cost_budget_micros=0,
fallback_limit=1,
)
)
store.close()
def _openclaw_floor_request(*, attempt=2):
return {
"schema":"aios.external-agent-runtime.request.v1",
"request_id":f"external-openclaw-floor-attempt-{attempt:02d}",
"runtime":"openclaw",
"task_id":"r42-model-governance-openclaw-live-20260818",
"attempt":attempt,
"execution_digest":"d"*64,
"payload":{
"goal":"Return AIOS_MODEL_GOVERNANCE_OPENCLAW_OK.",
"instructions":"No side effects.",
"success_criteria":[],
"constraints":{},
"scope":{
"scope_type":"tenant",
"tenant_id":"model-governance-live",
"project_id":"staging",
},
"trace_id":"trace-openclaw-model-governance-live",
},
}
def test_openclaw_profile_reservation_floor_covers_injected_context(
tmp_path,monkeypatch
):
helper_path=_openclaw_context_helper(tmp_path,input_tokens=7770,output_tokens=13)
profile_root=_openclaw_floor_profile(tmp_path,helper_path,floor=16384)
db=tmp_path/"openclaw-governance.sqlite3"
_seed_openclaw_floor_db(db)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
request_path=tmp_path/"request.json"
result_path=tmp_path/"result.json"
request_path.write_text(json.dumps(_openclaw_floor_request()),encoding="utf-8")
runtime.execute_request(request_path,result_path,profile_root=profile_root)
result=json.loads(result_path.read_text())
mg=result["model_governance"]
assert mg["input_reservation_floor_tokens"]==16384
assert mg["prompt_estimated_input_tokens"] < 16384
assert mg["reserved_estimated_input_tokens"]==16384
assert mg["settlement"]["state"]=="SETTLED"
assert mg["settlement"]["usage_source"]=="runtime"
assert mg["settlement"]["input_tokens"]==7770
assert mg["settlement"]["output_tokens"]==13
store=ModelGovernanceStore(db)
row=store.conn.execute(
"""
select reserved_input_tokens,state,actual_input_tokens,actual_output_tokens
from model_governance_reservations
where request_id=?
""",
(result["request_id"]+":model",),
).fetchone()
assert int(row["reserved_input_tokens"])==16384
assert row["state"]=="SETTLED"
assert int(row["actual_input_tokens"])==7770
assert int(row["actual_output_tokens"])==13
store.close()
def test_openclaw_reservation_floor_still_fails_closed_above_floor(
tmp_path,monkeypatch
):
helper_path=_openclaw_context_helper(tmp_path,input_tokens=17000,output_tokens=13)
profile_root=_openclaw_floor_profile(tmp_path,helper_path,floor=16384)
db=tmp_path/"openclaw-governance.sqlite3"
_seed_openclaw_floor_db(db)
monkeypatch.setattr(runtime,"MODEL_GOVERNANCE_ENABLED",True)
monkeypatch.setattr(runtime,"DEFAULT_MODEL_GOVERNANCE_DB",db)
request_path=tmp_path/"request.json"
result_path=tmp_path/"result.json"
request_path.write_text(json.dumps(_openclaw_floor_request()),encoding="utf-8")
with pytest.raises(
runtime.ExternalAgentRuntimeError,
match="MODEL_USAGE_EXCEEDS_RESERVATION",
):
runtime.execute_request(request_path,result_path,profile_root=profile_root)
assert not result_path.exists()
def test_model_governance_profile_rejects_invalid_input_reservation_floor(tmp_path):
path=tmp_path/"helper.py"
path.write_text("print('ok')\n",encoding="utf-8")
root=tmp_path/"profiles"
root.mkdir()
(root/"openclaw.json").write_text(json.dumps({
"schema":"aios.external-agent-runtime.profile.v1",
"runtime":"openclaw",
"argv":["/usr/bin/python3",str(path),"{model_id}"],
"input_mode":"stdin_text",
"output_mode":"stdout_text",
"model_governance":{
"required":True,
"provider_ids":["openai"],
"max_output_tokens":256,
"input_reservation_floor_tokens":262145,
},
}))
with pytest.raises(
runtime.ExternalAgentRuntimeError,
match="EXTERNAL_AGENT_MODEL_INPUT_RESERVATION_FLOOR_INVALID",
):
runtime.RuntimeProfile.load("openclaw",root)
@@ -0,0 +1,99 @@
from __future__ import annotations
import json
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
def test_executor_has_single_model_governance_sqlite_write_path():
unit=(ROOT/"deploy/systemd/aios-external-agent-executor.service").read_text()
assert "AIOS_MODEL_GOVERNANCE_DB=/var/lib/aios-model-governance/state.sqlite3" in unit
assert "AIOS_MODEL_GOVERNANCE_ENABLED=1" in unit
assert "ReadWritePaths=/var/lib/aios-model-governance" in unit
assert "ReadWritePaths=/var/lib/aios-external-agent-executor" in unit
assert "InaccessiblePaths=/etc/aios-gitops" in unit
assert "InaccessiblePaths=/data/aios-gitops" in unit
def test_tmpfiles_owner_matches_external_executor():
line=(ROOT/"deploy/tmpfiles.d/aios-model-governance.conf").read_text().strip()
assert line == (
"d /var/lib/aios-model-governance 0750 "
"aios-engineering-executor aios-executor-exchange -"
)
def test_staging_models_use_real_e2e_evaluation_evidence():
value=json.loads(
(ROOT/"config/model-governance/model-governance.staging.json").read_text()
)
assert value["schema"]=="aios.model-governance.bootstrap.v1"
models={item["model_id"]:item for item in value["models"]}
assert models["deepseek-v4-flash"]["evaluation_sha256"] == (
"43ae0db1640daeb6d33e2787769732844f366d36070065d3a2239905bd9a823b"
)
assert models["openai/deepseek-v4-flash"]["evaluation_sha256"] == (
"7b1c89634eb9c0e7cb5a3d75323d904ebbd91b3ae5494ce938bd8316a4cf5b12"
)
def test_runtime_profiles_bind_model_from_governance_not_task():
for name in ("hermes","openclaw"):
value=json.loads(
(ROOT/"config/external-agent-runtime.d"/f"{name}.profile.json").read_text()
)
governance=value["model_governance"]
assert governance["required"] is True
assert governance["provider_ids"]
assert governance["max_output_tokens"] > 0
serialized=json.dumps(value["argv"])+json.dumps(value.get("environment",{}))
assert "{model_id}" in serialized or "{provider_id}" in serialized
def test_staging_bootstrap_has_explicit_tenant_live_policy():
value=json.loads(
(ROOT/"config/model-governance/model-governance.staging.json").read_text()
)
keys={
(
item["scope_type"],
item.get("tenant_id"),
item.get("project_id"),
)
for item in value["policies"]
}
assert ("legacy",None,None) in keys
assert ("tenant","model-governance-live","staging") in keys
def test_staging_policy_input_limit_covers_root_owned_openclaw_reservation_floor():
config=json.loads(
(ROOT/"config/model-governance/model-governance.staging.json").read_text()
)
profile=json.loads(
(ROOT/"config/external-agent-runtime.d/openclaw.profile.json").read_text()
)
floor=int(profile["model_governance"]["input_reservation_floor_tokens"])
assert floor==16384
openclaw_model="openai/deepseek-v4-flash"
relevant=[
policy for policy in config["policies"]
if openclaw_model in policy["allowed_models"]
]
assert relevant
for policy in relevant:
assert int(policy["max_input_tokens_per_request"]) >= floor
def test_staging_openclaw_scope_limit_is_least_privilege_floor_not_model_ceiling():
config=json.loads(
(ROOT/"config/model-governance/model-governance.staging.json").read_text()
)
models={item["model_id"]:item for item in config["models"]}
model=models["openai/deepseek-v4-flash"]
assert int(model["max_input_tokens"])==32768
for policy in config["policies"]:
if "openai/deepseek-v4-flash" in policy["allowed_models"]:
assert int(policy["max_input_tokens_per_request"])==16384
assert int(policy["max_input_tokens_per_request"]) < int(model["max_input_tokens"])
@@ -0,0 +1,85 @@
from __future__ import annotations
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "active-runtime/app"))
from aios_online_agent_execution import ( # noqa: E402
GovernedExecutionBinding,
project_execution_envelope,
)
import aios_online_agent_worker as worker # noqa: F401,E402
from aios_online_agent_worker import WorkerSupervisor # noqa: E402
BASE = "a" * 40
def test_agent_runtime_replays_the_durable_envelope_to_bridge(monkeypatch) -> None:
monkeypatch.setattr(
worker,
"WORKER_CAPABILITIES",
tuple(sorted((*worker.WORKER_CAPABILITIES, "agent.runtime.hermes"))),
)
binding = GovernedExecutionBinding.from_mapping(
{
"task_id": "r42-agent-runtime-replay-test-20260818",
"job_id": "job-agent-runtime-replay-test",
"repository": "aios/aios",
"base_oid": BASE,
"expected_branch_oid": BASE,
"target_branch": "agent/agent-runtime-replay-test",
"branch_mode": "CREATE_NEW",
"generation": 1,
"idempotency_key": "agent-runtime-replay-test-v1",
}
)
execution = project_execution_envelope(
binding,
{
"action": "agent_runtime",
"adapter": "hermes",
"capability_scope": ["agent.runtime.hermes"],
"change_set": [],
"runtime_input": {
"goal": "replay the exact durable runtime envelope",
"instructions": "return the bounded sentinel",
"success_criteria": [
{
"id": "sentinel",
"description": "return the bounded sentinel",
}
],
"constraints": {
"provider_boundary": "provider-credential-only",
"change_set": [],
"writer_lease": False,
},
},
},
)
task = {
**binding.public(),
"required_capabilities": ["agent.runtime.hermes"],
"metadata": {"execution": execution},
}
class Bridge:
def __init__(self) -> None:
self.calls: list[object] = []
def project_execution(self, _task: object, projection_input: object = None):
self.calls.append(projection_input)
return 200, dict(projection_input or {})
supervisor = object.__new__(WorkerSupervisor)
supervisor.bridge = Bridge()
resolved, source = supervisor.resolve_envelope(task)
assert source == "persisted_verified"
assert resolved.action == "agent_runtime"
assert resolved.adapter == "hermes"
assert resolved.digest == execution["execution_digest"]
assert supervisor.bridge.calls == [execution]
@@ -0,0 +1,56 @@
from __future__ import annotations
from pathlib import Path
from aios_online_agent_checkpoint import CheckpointBinding, CheckpointStore
def _binding() -> CheckpointBinding:
return CheckpointBinding(
task_id="checkpoint-agent-runtime-test-20260818",
attempt=1,
execution_digest="a" * 64,
job_id="job-checkpoint-agent-runtime-test-20260818",
repository="aios/aios",
base_oid="b" * 40,
expected_branch_oid="b" * 40,
target_branch="agent/checkpoint-agent-runtime-test-20260818",
generation=1,
)
def test_agent_runtime_checkpoint_phase_is_durable_before_result(tmp_path: Path) -> None:
store = CheckpointStore(tmp_path / "checkpoints.sqlite3")
binding = _binding()
phases = [
"claimed",
"workspace_prepared",
"verification_passed",
"agent_runtime_done",
"result_pending",
"result_submitted",
]
rows = [
store.append(binding, phase, {"phase": phase, "task_id": binding.task_id})
for phase in phases
]
assert [row["phase"] for row in rows] == phases
assert [int(row["phase_order"]) for row in rows] == [10, 20, 30, 40, 50, 60]
latest = store.latest_for_attempt(binding.task_id, binding.attempt)
assert latest is not None
assert latest["phase"] == "result_submitted"
assert latest["payload"] == {"phase": "result_submitted", "task_id": binding.task_id}
def test_agent_runtime_checkpoint_append_is_idempotent(tmp_path: Path) -> None:
store = CheckpointStore(tmp_path / "checkpoints.sqlite3")
binding = _binding()
payload = {"phase": "agent_runtime_done", "result": {"status": "SUCCEEDED"}}
first = store.append(binding, "agent_runtime_done", payload)
replay = store.append(binding, "agent_runtime_done", payload)
assert replay["sequence"] == first["sequence"]
assert replay["payload_sha256"] == first["payload_sha256"]
@@ -0,0 +1,209 @@
from __future__ import annotations
from pathlib import Path
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
import aios_online_agent_worker as worker
from aios_online_agent_execution import (
GovernedExecutionBinding,
project_execution_envelope,
)
@pytest.fixture(autouse=True)
def _state_paths(monkeypatch, tmp_path):
root=tmp_path/"worker-state"
monkeypatch.setattr(worker,"STATE_ROOT",root)
monkeypatch.setattr(worker,"WORKER_LIVENESS_PATH",root/"worker-liveness.json")
BASE="a"*40
TASK_ID="task-worker-availability-placeholder"
JOB_ID="job-worker-availability-placeholder"
BRANCH="agent/task-worker-availability-placeholder"
IDEMPOTENCY="task-worker-availability-placeholder-binding-v1"
def binding():
return GovernedExecutionBinding.from_mapping({
"task_id":TASK_ID,
"job_id":JOB_ID,
"repository":"aios/aios",
"base_oid":BASE,
"expected_branch_oid":BASE,
"target_branch":BRANCH,
"branch_mode":"CREATE_NEW",
"generation":1,
"idempotency_key":IDEMPOTENCY,
})
def placeholder():
return project_execution_envelope(
binding(),
{
"action":"source_capture",
"adapter":"source_capture",
"capability_scope":[
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
],
},
)
def readonly_source_capture():
return project_execution_envelope(
binding(),
{
"action":"source_capture",
"adapter":"source_capture",
"capability_scope":["git.read","git.test"],
"source_signals":["worker availability"],
},
)
def deterministic():
return project_execution_envelope(
binding(),
{
"action":"deterministic",
"adapter":"deterministic",
"capability_scope":[
"git.read",
"git.test",
"governed.write.branch",
"governed.write.file",
],
"change_set":[{
"path":"active-runtime/app/example.py",
"operation":"write_file",
"content":"VALUE=1\\n",
}],
"verification":[{
"label":"unit",
"argv":["/usr/bin/python3","-m","pytest","-q"],
"cwd":".",
"timeout_seconds":120,
}],
"governance":{
"commit_idempotency_key":"task-worker-availability-placeholder-commit-v1",
"allowed_files":["active-runtime/app/example.py"],
},
},
)
def task(execution):
return {
"task_id":TASK_ID,
"required_capabilities":list(execution["capability_scope"]),
"metadata":{
"execution":execution,
"execution_projection":{
"schema":"aios.online-agent.task-projection.v1",
"task_id":TASK_ID,
"job_id":JOB_ID,
"execution_digest":execution["execution_digest"],
},
},
}
class FakeBridge:
def __init__(self,status,execution):
self.status=status
self.execution=execution
self.calls=[]
def project_execution(self,task_value,execution=None):
self.calls.append(execution)
return self.status,self.execution
class FakeAgent:
def __init__(self,tasks):
self.tasks=tasks
self.claim_calls=[]
def poll(self):
return list(self.tasks)
def claim(self,task_id):
self.claim_calls.append(task_id)
return 500,{"error":"SHOULD_NOT_CLAIM"}
def supervisor(bridge, agent=None):
value=object.__new__(worker.WorkerSupervisor)
value.bridge=bridge
value.agent=agent
return value
def test_write_capable_source_capture_is_classified_as_placeholder():
envelope=worker.ExecutionEnvelope.from_task(
task(placeholder()),
worker_capabilities=tuple(placeholder()["capability_scope"]),
)
assert worker.source_capture_requires_deterministic_plan(envelope) is True
def test_readonly_source_capture_is_not_placeholder():
envelope=worker.ExecutionEnvelope.from_task(
task(readonly_source_capture()),
worker_capabilities=tuple(readonly_source_capture()["capability_scope"]),
)
assert worker.source_capture_requires_deterministic_plan(envelope) is False
def test_placeholder_without_registered_plan_is_skipped_before_claim():
persisted=placeholder()
bridge=FakeBridge(200,persisted)
agent=FakeAgent([task(persisted)])
sup=supervisor(bridge,agent)
assert sup.once()==0
assert bridge.calls==[None]
assert agent.claim_calls==[]
def test_placeholder_with_missing_projection_is_skipped_before_claim():
persisted=placeholder()
bridge=FakeBridge(404,{"error":"TASK_EXECUTION_PROJECTION_MISSING"})
agent=FakeAgent([task(persisted)])
sup=supervisor(bridge,agent)
assert sup.once()==0
assert bridge.calls==[None]
assert agent.claim_calls==[]
def test_registered_deterministic_plan_replaces_placeholder():
persisted=placeholder()
resolved=deterministic()
bridge=FakeBridge(200,resolved)
sup=supervisor(bridge)
envelope,source=sup.resolve_envelope(task(persisted))
assert source=="plan_registry_verified"
assert envelope.action=="deterministic"
assert envelope.digest==resolved["execution_digest"]
assert bridge.calls==[None]
def test_real_readonly_source_capture_replays_exact_payload():
persisted=readonly_source_capture()
bridge=FakeBridge(200,persisted)
sup=supervisor(bridge)
envelope,source=sup.resolve_envelope(task(persisted))
assert source=="persisted_verified"
assert envelope.action=="source_capture"
assert envelope.digest==persisted["execution_digest"]
assert bridge.calls==[persisted]
@@ -0,0 +1,16 @@
from __future__ import annotations
import json
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
def test_openclaw_profile_uses_conservative_input_reservation_floor():
value=json.loads(
(ROOT/"config/external-agent-runtime.d/openclaw.profile.json").read_text()
)
mg=value["model_governance"]
assert mg["required"] is True
assert mg["input_reservation_floor_tokens"]==16384
assert mg["max_output_tokens"]==256
@@ -0,0 +1,83 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_capability_contract import ( # noqa: E402
has_capabilities,
normalize_required_capabilities,
normalize_worker_capabilities,
normalized_capability_matrix,
required_capabilities_subset,
)
def test_release_candidate_is_known_to_task_and_worker_vocabularies():
capability="release:candidate"
assert normalize_required_capabilities([capability])==frozenset({capability})
assert normalize_worker_capabilities([capability])==frozenset({capability})
assert has_capabilities([capability],[capability]) is True
assert normalized_capability_matrix([capability],[capability])=={
"worker_effective":["release:candidate"],
"task_required":["release:candidate"],
"eligible":True,
}
def test_release_candidate_capability_does_not_imply_high_risk_release_operations():
worker=["release:candidate"]
for forbidden in (
"submitOwnerApproval",
"startDeployment",
"tag_publish",
"baseline_registry_finalize",
"runtime_switch",
"deploy.production",
):
assert has_capabilities(worker,[forbidden]) is False
def test_release_wildcards_and_nearby_unknown_names_remain_fail_closed():
assert has_capabilities(["release:candidate"],["release:*"]) is False
assert has_capabilities(["release:candidate"],["release:candidate.extra"]) is False
assert has_capabilities(["release:*"],["release:candidate"]) is False
def test_release_candidate_delegation_subset_is_exact():
assert required_capabilities_subset(
["release:candidate"],
["release:candidate"],
) is True
assert required_capabilities_subset(
["release:candidate"],
["startDeployment"],
) is False
@pytest.mark.parametrize(
"worker,required",
[
(["agent.runtime.hermes"],["agent.runtime.hermes"]),
(["agent.runtime.openclaw"],["agent.runtime.openclaw"]),
(["git.read"],["repository_read"]),
(["git.test"],["test_execution"]),
(
["governed.write.branch","governed.write.file"],
["governed.git.write"],
),
],
)
def test_existing_known_capability_semantics_are_non_regressed(worker,required):
assert has_capabilities(worker,required) is True
def test_release_candidate_worker_does_not_match_empty_or_unrelated_task_by_escalation():
assert has_capabilities(["release:candidate"],[]) is True
assert has_capabilities(["release:candidate"],["git.read"]) is False
assert has_capabilities(["release:candidate"],["agent.runtime.hermes"]) is False
+95
View File
@@ -0,0 +1,95 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_release_lineage_contract import ( # noqa: E402
FIXED_V2R2C2,
ReleaseLineageError,
validate_fixed_v2r2c2_task,
)
def fixed_task(source=FIXED_V2R2C2.source_commit, *, status="FAILED", attempt=1, max_attempts=1, revision=4):
return {
"task_id":FIXED_V2R2C2.task_id,
"status":status,
"attempt":attempt,
"max_attempts":max_attempts,
"revision":revision,
"required_capabilities":["release:candidate"],
"constraints":{
"release_candidate":{
"candidate_id":FIXED_V2R2C2.candidate_id,
"source_commit":source,
"version":FIXED_V2R2C2.version,
"projection_id":"projection-"+"a"*32,
}
},
}
def test_fixed_v2r2c2_lineage_is_the_original_release_worker_candidate():
assert FIXED_V2R2C2.source_commit=="71e736b05fab13efc06af7a4fe2dc022c1cf9a85"
result=validate_fixed_v2r2c2_task(fixed_task())
assert result["status"]=="VERIFIED"
assert result["lineage"]["version"]=="V2R2C2"
def test_later_capability_remediation_is_not_retroactively_v2r2c2_source():
with pytest.raises(ReleaseLineageError,match="FIXED_RELEASE_LINEAGE_DRIFT"):
validate_fixed_v2r2c2_task(
fixed_task("17deb91ca7a187dc81766945b1174e854f86bd25")
)
def test_acp_engineering_candidate_is_not_retroactively_v2r2c2_source():
with pytest.raises(ReleaseLineageError,match="FIXED_RELEASE_LINEAGE_DRIFT"):
validate_fixed_v2r2c2_task(
fixed_task("b4b4416c93fedbe451d655d49e28f915b8fd38e6")
)
@pytest.mark.parametrize(
"status,attempt,max_attempts,revision",
[
("FAILED",1,1,4),
("QUEUED",1,2,5),
("CLAIMED",2,2,6),
("VERIFYING",2,2,9),
("SUCCEEDED",2,2,10),
],
)
def test_lineage_contract_does_not_confuse_lifecycle_state_with_release_identity(
status,attempt,max_attempts,revision
):
result=validate_fixed_v2r2c2_task(
fixed_task(
status=status,
attempt=attempt,
max_attempts=max_attempts,
revision=revision,
)
)
assert result["status"]=="VERIFIED"
assert result["task_status"]==status
assert result["attempt"]==attempt
assert result["max_attempts"]==max_attempts
assert result["revision"]==revision
def test_capability_and_task_identity_are_bound():
value=fixed_task()
value["required_capabilities"]=[]
with pytest.raises(ReleaseLineageError,match="FIXED_TASK_CAPABILITY_DRIFT"):
validate_fixed_v2r2c2_task(value)
value=fixed_task()
value["task_id"]="replacement-v2r2c2"
with pytest.raises(ReleaseLineageError,match="FIXED_TASK_ID_DRIFT"):
validate_fixed_v2r2c2_task(value)
+226
View File
@@ -0,0 +1,226 @@
from __future__ import annotations
import json
from pathlib import Path
import sys
import pytest
ROOT=Path(__file__).resolve().parents[1]
APP=ROOT/"active-runtime/app"
sys.path.insert(0,str(APP))
import aios_release_online_agent_worker as worker
def task(**overrides):
value={
"task_id":"r42-release-candidate-live-acceptance-20260819",
"required_capabilities":["release:candidate"],
"constraints":{
"release_candidate":{
"candidate_id":"r42-release-candidate-v2r2c2-20260819",
"source_commit":"5"*40,
"version":"V2R2C2",
"projection_id":"projection-"+"a"*32,
}
}
}
value.update(overrides)
return value
def test_release_request_accepts_only_exact_release_capability():
req=worker.ReleaseRequest.from_task(task())
assert req.candidate_id=="r42-release-candidate-v2r2c2-20260819"
assert req.source_commit=="5"*40
assert req.version=="V2R2C2"
assert req.projection_id=="projection-"+"a"*32
with pytest.raises(worker.ReleaseWorkerError,match="TASK_CAPABILITY_SCOPE_INVALID"):
worker.ReleaseRequest.from_task(task(required_capabilities=["release:candidate","git.read"]))
@pytest.mark.parametrize(
"payload",
[
{
"candidate_id":"r42-release-candidate-v2r2c2-20260819",
"source_commit":"short",
"version":"V2R2C2",
"projection_id":"projection-"+"a"*32,
},
{
"candidate_id":"bad",
"source_commit":"5"*40,
"version":"V2R2C2",
"projection_id":"projection-"+"a"*32,
},
{
"candidate_id":"r42-release-candidate-v2r2c2-20260819",
"source_commit":"5"*40,
"version":"bad",
"projection_id":"projection-"+"a"*32,
},
{
"candidate_id":"r42-release-candidate-v2r2c2-20260819",
"source_commit":"5"*40,
"version":"V2R2C2",
"projection_id":"bad",
},
{
"candidate_id":"r42-release-candidate-v2r2c2-20260819",
"source_commit":"5"*40,
"version":"V2R2C2",
"projection_id":"projection-"+"a"*32,
"execute":True,
},
],
)
def test_release_request_rejects_invalid_or_extra_fields(payload):
value=task()
value["constraints"]={"release_candidate":payload}
with pytest.raises(worker.ReleaseWorkerError):
worker.ReleaseRequest.from_task(value)
def test_result_payload_preserves_high_risk_denials():
release=worker.ReleaseRequest.from_task(task())
result=worker.result_payload(
"r42-release-candidate-live-acceptance-20260819",
release,
{"candidate_id":release.candidate_id,"status":"BUILT"},
)
assert result["status"]=="SUCCEEDED"
assert result["classification"]=="RELEASE_CANDIDATE_CREATED"
assert result["writer_lease"]=="NONE"
assert result["changed_files"]==[]
assert result["direct_main_write"] is False
assert result["production_write"] is False
assert result["tag_write"] is False
assert result["baseline_write"] is False
assert result["runtime_switch"] is False
assert result["owner_approval_submitted"] is False
assert result["credential_material_exposed"] is False
def test_result_payload_rejects_builder_candidate_mismatch():
release=worker.ReleaseRequest.from_task(task())
with pytest.raises(worker.ReleaseWorkerError,match="BUILDER_CANDIDATE_BINDING_INVALID"):
worker.result_payload(
"r42-release-candidate-live-acceptance-20260819",
release,
{"candidate_id":"other-release-candidate"},
)
def test_worker_calls_only_fixed_release_candidate_route(monkeypatch):
release=worker.ReleaseRequest.from_task(task())
calls=[]
def fake_http(base,method,route,token,body=None,timeout=30):
calls.append((base,method,route,token,body))
if route=="/health":
return 200,{"status":"ok"}
if route=="/createReleaseCandidate" and body=={}:
return 400,{"error":"request_invalid"}
if route.endswith("/claim"):
return 200,{"lease_token":"l"*32,"attempt":1}
if route.endswith("/heartbeat"):
return 200,{}
if route=="/createReleaseCandidate":
return 200,{
"candidate_id":release.candidate_id,
"artifact_sha256":"f"*64,
}
if route.endswith("/result"):
return 200,{"status":"VERIFYING"}
raise AssertionError(route)
monkeypatch.setattr(worker,"http_json",fake_http)
monkeypatch.setattr(worker,"audit",lambda *args,**kwargs:None)
monkeypatch.setattr(worker,"write_liveness",lambda *args,**kwargs:None)
monkeypatch.setattr(worker.Heartbeat,"first",lambda self:None)
monkeypatch.setattr(worker.Heartbeat,"close",lambda self:None)
monkeypatch.setattr(worker.Heartbeat,"ensure",lambda self:None)
monkeypatch.setattr(worker.threading.Thread,"start",lambda self:None)
assert worker.execute_task(
task(),
agent_token="a"*32,
release_token="r"*32,
) is True
release_calls=[
item for item in calls
if item[0]==worker.RELEASE_API
and item[2]=="/createReleaseCandidate"
and item[4]!={}
]
assert len(release_calls)==1
assert release_calls[0][1]=="POST"
assert release_calls[0][2]=="/createReleaseCandidate"
assert release_calls[0][4]=={
"candidate_id":release.candidate_id,
"source_commit":release.source_commit,
"version":release.version,
}
def test_write_liveness_records_release_capability(tmp_path,monkeypatch):
path=tmp_path/"worker-liveness.json"
monkeypatch.setattr(worker,"STATE_ROOT",tmp_path)
monkeypatch.setattr(worker,"LIVENESS_PATH",path)
worker.write_liveness(
"IDLE",
stage="poll",
visible_count=2,
)
value=json.loads(path.read_text())
assert value["schema"]=="aios.online-agent.worker-liveness.v1"
assert value["agent_type"]=="release-preparation-worker"
assert value["capabilities"]==["release:candidate"]
assert value["state"]=="IDLE"
assert value["visible_count"]==2
def test_sd_notify_is_noop_without_systemd_socket(monkeypatch):
monkeypatch.delenv("NOTIFY_SOCKET",raising=False)
worker.sd_notify("WATCHDOG=1")
def test_systemd_unit_uses_two_encrypted_credentials_and_loopback_release_api():
unit=(ROOT/"deploy/systemd/aios-release-online-agent-worker.service").read_text()
assert "LoadCredentialEncrypted=agent_token:" in unit
assert "LoadCredentialEncrypted=release_candidate_token:" in unit
assert "AIOS_RELEASE_API_URL=http://127.0.0.1:18890" in unit
assert "AIOS_RELEASE_WORKER_AGENT_ID=online-release-worker-cn-network-mkt-20260819" in unit
assert "Type=notify" in unit
assert "WatchdogSec=90" in unit
assert "Restart=always" in unit
assert "IPAddressDeny=any" in unit
assert "IPAddressAllow=127.0.0.0/8" in unit
assert "InaccessiblePaths=/data/aios-gitops /home" in unit
assert "submitOwnerApproval" not in unit
assert "startDeployment" not in unit
def test_source_contains_no_high_risk_release_routes():
source=(ROOT/"active-runtime/app/aios_release_online_agent_worker.py").read_text()
assert '"/createReleaseCandidate"' in source
for forbidden in (
"/submitOwnerApproval",
"/startDeployment",
"/requestOrCreateTag",
"/buildProductionArtifact",
"/executeApprovedProductionRelease",
):
assert forbidden not in source
def test_docs_keep_release_candidate_distinct_from_production():
doc=(ROOT/"docs/superpowers/specs/2026-08-19-release-preparation-online-agent.md").read_text()
assert "release:candidate" in doc
assert "Owner signature" in doc
assert "never\nstarts deployment" in doc or "never starts deployment" in doc
assert "LoadCredentialEncrypted" in doc
@@ -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
+199
View File
@@ -0,0 +1,199 @@
from __future__ import annotations
import json
from pathlib import Path
import sys
ROOT=Path(__file__).resolve().parents[1]
sys.path.insert(0,str(ROOT/"active-runtime/app"))
from aios_worker_availability_guard import AvailabilityGuard
class FakeSystemd:
def __init__(self, roots, now=1000.0):
self.now=now
self.actions=[]
self.units={}
for unit,root in roots.items():
self.units[unit]={
"LoadState":"loaded",
"ActiveState":"active",
"UnitFileState":"enabled",
"Environment":f"AIOS_WORKER_STATE_ROOT={root}",
}
self.units["aios-external-agent-executor.service"]={
"LoadState":"loaded",
"ActiveState":"active",
"UnitFileState":"enabled",
"Environment":"",
}
def property(self, unit, name):
return self.units.get(unit,{}).get(name,"")
def environment(self, unit):
raw=self.property(unit,"Environment")
result={}
for item in raw.split():
if "=" in item:
k,v=item.split("=",1)
result[k]=v
return result
def enable(self, unit):
self.actions.append((unit,"enable"))
self.units[unit]["UnitFileState"]="enabled"
def start(self, unit):
self.actions.append((unit,"start"))
self.units[unit]["ActiveState"]="active"
env=self.environment(unit)
if "AIOS_WORKER_STATE_ROOT" not in env:
return
root=Path(env["AIOS_WORKER_STATE_ROOT"])
root.mkdir(parents=True,exist_ok=True)
(root/"worker-liveness.json").write_text(json.dumps({
"schema":"aios.online-agent.worker-liveness.v1",
"agent_id":"agent-"+unit,
"state":"POLL_OK",
"last_seen_epoch":self.now,
}),encoding="utf-8")
def restart(self, unit):
self.actions.append((unit,"restart"))
self.units[unit]["ActiveState"]="active"
env=self.environment(unit)
root=Path(env["AIOS_WORKER_STATE_ROOT"])
root.mkdir(parents=True,exist_ok=True)
(root/"worker-liveness.json").write_text(json.dumps({
"schema":"aios.online-agent.worker-liveness.v1",
"agent_id":"agent-"+unit,
"state":"POLL_OK",
"last_seen_epoch":self.now,
}),encoding="utf-8")
def make_guard(tmp_path, fake):
worker_units=tuple(
unit for unit in fake.units
if unit!="aios-external-agent-executor.service"
)
return AvailabilityGuard(
systemd=fake,
worker_units=worker_units,
dependency_units=("aios-external-agent-executor.service",),
status_path=tmp_path/"run/status.json",
recovery_path=tmp_path/"run/recovery.json",
inhibit_path=tmp_path/"run/maintenance.lock",
max_stale_seconds=90,
restart_cooldown_seconds=60,
startup_wait_seconds=0,
now_fn=lambda:fake.now,
sleep_fn=lambda _seconds:None,
)
def write_liveness(root, now, *, state="POLL_OK"):
root.mkdir(parents=True,exist_ok=True)
(root/"worker-liveness.json").write_text(json.dumps({
"schema":"aios.online-agent.worker-liveness.v1",
"agent_id":"agent",
"state":state,
"last_seen_epoch":now,
}),encoding="utf-8")
def test_healthy_workers_need_no_action(tmp_path):
roots={
"generic.service":tmp_path/"generic",
"hermes.service":tmp_path/"hermes",
"openclaw.service":tmp_path/"openclaw",
}
fake=FakeSystemd(roots)
for root in roots.values():
write_liveness(root,fake.now)
result=make_guard(tmp_path,fake).run(heal=True)
assert result["status"]=="HEALTHY"
assert result["all_fresh"] is True
assert fake.actions==[]
def test_inactive_worker_is_started_and_becomes_fresh(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
fake.units["generic.service"]["ActiveState"]="inactive"
result=make_guard(tmp_path,fake).run(heal=True)
assert ("generic.service","start") in fake.actions
assert result["all_fresh"] is True
def test_disabled_worker_is_enabled_and_started(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
fake.units["generic.service"]["UnitFileState"]="disabled"
fake.units["generic.service"]["ActiveState"]="inactive"
result=make_guard(tmp_path,fake).run(heal=True)
assert ("generic.service","enable") in fake.actions
assert ("generic.service","start") in fake.actions
assert result["all_fresh"] is True
def test_active_but_stale_worker_is_restarted(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
write_liveness(roots["generic.service"],fake.now-500)
result=make_guard(tmp_path,fake).run(heal=True)
assert ("generic.service","restart") in fake.actions
assert result["all_fresh"] is True
assert result["workers"][0]["action"]=="RESTART_STALE"
def test_restart_cooldown_prevents_restart_storm(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
write_liveness(roots["generic.service"],fake.now-500)
guard=make_guard(tmp_path,fake)
guard.recovery_path.parent.mkdir(parents=True,exist_ok=True)
guard.recovery_path.write_text(json.dumps({
"schema":"aios.worker-availability.recovery.v1",
"last_restart_epoch":{"generic.service":fake.now-10},
}),encoding="utf-8")
result=guard.run(heal=True)
assert ("generic.service","restart") not in fake.actions
assert result["status"]=="DEGRADED"
assert result["workers"][0]["detail"]=="RESTART_COOLDOWN_ACTIVE"
def test_maintenance_inhibit_disables_healing(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
fake.units["generic.service"]["ActiveState"]="inactive"
guard=make_guard(tmp_path,fake)
guard.inhibit_path.parent.mkdir(parents=True,exist_ok=True)
guard.inhibit_path.write_text("planned maintenance\n")
result=guard.run(heal=True)
assert result["status"]=="MAINTENANCE_INHIBITED"
assert fake.actions==[]
def test_dependency_is_enabled_and_started(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
write_liveness(roots["generic.service"],fake.now)
fake.units["aios-external-agent-executor.service"]["UnitFileState"]="disabled"
fake.units["aios-external-agent-executor.service"]["ActiveState"]="inactive"
result=make_guard(tmp_path,fake).run(heal=True)
assert ("aios-external-agent-executor.service","enable") in fake.actions
assert ("aios-external-agent-executor.service","start") in fake.actions
assert result["all_fresh"] is True
def test_check_reports_stale_without_mutation(tmp_path):
roots={"generic.service":tmp_path/"generic"}
fake=FakeSystemd(roots)
write_liveness(roots["generic.service"],fake.now-500)
result=make_guard(tmp_path,fake).run(heal=False)
assert result["status"]=="DEGRADED"
assert result["all_fresh"] is False
assert fake.actions==[]
@@ -0,0 +1,56 @@
from __future__ import annotations
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1]
def test_worker_source_has_idle_and_busy_liveness():
source=(ROOT/"active-runtime/app/aios_online_agent_worker.py").read_text()
assert "aios.online-agent.worker-liveness.v1" in source
assert 'write_worker_liveness("POLL_OK"' in source
assert '"POLL_FAILED"' in source
assert 'write_worker_liveness("STARTING")' in source
assert '"BUSY"' in source
assert "last_seen_epoch" in source
def test_guard_timer_is_persistent_and_frequent():
timer=(ROOT/"deploy/systemd/aios-worker-availability-guard.timer").read_text()
assert "OnBootSec=15s" in timer
assert "OnUnitActiveSec=30s" in timer
assert "Persistent=true" in timer
assert "WantedBy=timers.target" in timer
def test_guard_service_has_no_network_or_credentials():
unit=(ROOT/"deploy/systemd/aios-worker-availability-guard.service").read_text()
assert "User=root" in unit
assert "LoadCredential" not in unit
assert "RestrictAddressFamilies=AF_UNIX" in unit
assert "NoNewPrivileges=true" in unit
assert "ProtectSystem=strict" in unit
assert "ReadWritePaths=/run/aios-worker-availability" in unit
assert "AIOS_WORKER_AVAILABILITY_MAX_STALE_SECONDS=90" in unit
def test_guard_manages_all_three_workers_and_external_executor():
source=(ROOT/"active-runtime/app/aios_worker_availability_guard.py").read_text()
for unit in (
"aios-online-agent-worker-20260815.service",
"aios-hermes-online-agent-worker.service",
"aios-openclaw-online-agent-worker.service",
"aios-external-agent-executor.service",
):
assert unit in source
assert "maintenance.lock" in source
assert "RESTART_STALE" in source
def test_worker_defers_write_capable_source_capture_until_plan_exists():
source=(ROOT/"active-runtime/app/aios_online_agent_worker.py").read_text()
assert "ENGINEERING_WRITE_CAPABILITIES" in source
assert "source_capture_requires_deterministic_plan" in source
assert "TASK_SKIPPED_PENDING_DETERMINISTIC_PLAN" in source
assert '"governed.write.branch"' in source
assert '"governed.write.file"' in source