Initialize repository snapshot

This commit is contained in:
Vault Sovereign
2025-12-27 00:10:32 +00:00
commit 110d644e10
281 changed files with 40331 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
"""
VaultMesh Console Engine
AI agent session management, code operations, and sovereign development.
"""
from .receipts import (
ConsoleReceiptEmitter,
emit_console_receipt,
ReceiptType,
get_emitter,
)
from .approvals import (
ApprovalManager,
ApprovalRequest,
get_approval_manager,
)
__all__ = [
"ConsoleReceiptEmitter",
"emit_console_receipt",
"ReceiptType",
"get_emitter",
"ApprovalManager",
"ApprovalRequest",
"get_approval_manager",
]

View File

@@ -0,0 +1,209 @@
"""
Console Engine Approval Manager
Handles approval requests, pending state, and decision recording.
State is derived from scroll on init, kept in memory during runtime.
"""
from __future__ import annotations
from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Any, Dict, List, Optional, Literal
import json
from .receipts import emit_console_receipt, get_emitter
ApprovalStatus = Literal["pending", "approved", "rejected", "expired"]
@dataclass
class ApprovalRequest:
"""An approval request waiting for decision."""
approval_id: str
session_id: str
action_type: str
action_details: Dict[str, Any]
requested_by: str
approvers: List[str]
expires_at: str
status: ApprovalStatus = "pending"
created_at: str = field(
default_factory=lambda: datetime.now(timezone.utc).isoformat()
)
class ApprovalManager:
"""
Manages pending approval requests.
State is derived from scroll on init, kept in memory during runtime.
"""
def __init__(self, vaultmesh_root: Optional[str] = None):
self.vaultmesh_root = vaultmesh_root
self._pending: Dict[str, ApprovalRequest] = {}
self._load_pending_from_scroll()
# --------------------------------------------------------------------- #
# Internal helpers
# --------------------------------------------------------------------- #
def _load_pending_from_scroll(self) -> None:
"""
Reconstruct pending approvals from Console scroll.
Algorithm:
- Scan all receipts.
- Collect all console_approval_request receipts as candidates.
- Remove any whose approval_id appears in a console_approval receipt.
- Drop any that are expired.
"""
emitter = get_emitter(self.vaultmesh_root)
events_path = Path(emitter.events_path)
if not events_path.exists():
return
requests: Dict[str, ApprovalRequest] = {}
decided_ids: set = set()
for line in events_path.read_text(encoding="utf-8").splitlines():
if not line.strip():
continue
try:
r = json.loads(line)
except Exception:
continue
r_type = r.get("type")
payload = r.get("payload") or {}
if r_type == "console_approval_request":
approval_id = payload.get("approval_id")
if not approval_id:
continue
req = ApprovalRequest(
approval_id=approval_id,
session_id=payload.get("session_id") or r.get("session_id"),
action_type=payload.get("action_type", ""),
action_details=payload.get("action_details", {}),
requested_by=payload.get("requested_by", ""),
approvers=payload.get("approvers", []),
expires_at=payload.get("expires_at", ""),
status=payload.get("status", "pending"),
created_at=payload.get("created_at", r.get("ts")),
)
requests[approval_id] = req
elif r_type == "console_approval":
approval_id = payload.get("approval_id")
if approval_id:
decided_ids.add(approval_id)
# Filter to pending and not expired
now = datetime.now(timezone.utc)
for approval_id, req in requests.items():
if approval_id in decided_ids:
continue
try:
exp = datetime.fromisoformat(req.expires_at.replace("Z", "+00:00"))
except Exception:
exp = now
if exp < now:
continue # expired; skip
req.status = "pending"
self._pending[approval_id] = req
# --------------------------------------------------------------------- #
# Public API
# --------------------------------------------------------------------- #
def request_approval(
self,
session_id: str,
action_type: str,
action_details: Dict[str, Any],
requested_by: str,
approvers: List[str],
timeout_minutes: int = 60,
) -> ApprovalRequest:
"""Create a new approval request and emit receipt."""
now = datetime.now(timezone.utc)
approval_id = f"approval-{now.strftime('%Y-%m-%d-%H%M%S')}"
expires_at = (now + timedelta(minutes=timeout_minutes)).isoformat()
request = ApprovalRequest(
approval_id=approval_id,
session_id=session_id,
action_type=action_type,
action_details=action_details,
requested_by=requested_by,
approvers=approvers,
expires_at=expires_at,
)
emit_console_receipt(
"console_approval_request",
asdict(request),
session_id=session_id,
)
self._pending[approval_id] = request
return request
def decide(
self,
approval_id: str,
approved: bool,
approver: str,
reason: str = "",
) -> bool:
"""Record approval decision and emit receipt."""
if approval_id not in self._pending:
# Let caller decide whether to treat this as error
raise KeyError(f"Approval not found: {approval_id}")
request = self._pending[approval_id]
if approver not in request.approvers:
raise PermissionError(f"{approver} not in approver pool")
request.status = "approved" if approved else "rejected"
emit_console_receipt(
"console_approval",
{
"approval_id": approval_id,
"action_type": request.action_type,
"approved": approved,
"approver": approver,
"reason": reason,
"decided_at": datetime.now(timezone.utc).isoformat(),
},
session_id=request.session_id,
)
del self._pending[approval_id]
return True
def list_pending(self, session_id: Optional[str] = None) -> List[ApprovalRequest]:
"""List pending approval requests."""
requests = list(self._pending.values())
if session_id:
requests = [r for r in requests if r.session_id == session_id]
return requests
# Singleton
_manager: Optional[ApprovalManager] = None
def get_approval_manager(vaultmesh_root: Optional[str] = None) -> ApprovalManager:
"""Get or create the ApprovalManager singleton."""
global _manager
if _manager is None:
_manager = ApprovalManager(vaultmesh_root)
return _manager

271
engines/console/receipts.py Normal file
View File

@@ -0,0 +1,271 @@
"""
Console Engine Receipt Emitter
Appends receipts to the Console scroll and maintains the per-engine Merkle root.
"""
import json
import os
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, TypedDict
try:
import blake3 # type: ignore
except ImportError:
blake3 = None # fallback to hashlib
ENGINE_ID = "engine:console"
# Paths relative to VaultMesh repo root
DEFAULT_EVENTS_PATH = "receipts/console/console_events.jsonl"
DEFAULT_ROOT_PATH = "receipts/console/ROOT.console.txt"
ReceiptType = Literal[
"console_genesis",
"console_session_start",
"console_session_end",
"console_command",
"console_file_edit",
"console_tool_call",
"console_approval_request", # Request for approval (pending)
"console_approval", # Decision on approval request
"console_git_commit",
"console_agent_spawn",
]
class ConsoleReceipt(TypedDict):
"""Schema for Console receipts."""
ts: str
engine_id: str
type: ReceiptType
session_id: Optional[str]
payload: Dict[str, Any]
@dataclass
class ConsoleReceiptEmitter:
"""
Local filesystem emitter for Console receipts.
Appends receipts to console_events.jsonl and updates ROOT.console.txt
with the computed Merkle root.
NOTE: This uses a simple O(n) recompute for the Merkle root.
TODO: Replace with shared receipts frontier or Rust FFI for O(log n) updates.
"""
events_path: str = DEFAULT_EVENTS_PATH
root_path: str = DEFAULT_ROOT_PATH
vaultmesh_root: Optional[str] = None
def __post_init__(self):
"""Resolve paths relative to VaultMesh root."""
if self.vaultmesh_root is None:
# Try to auto-detect from environment or use current directory
self.vaultmesh_root = os.environ.get(
"VAULTMESH_ROOT",
os.getcwd()
)
# Make paths absolute
root = Path(self.vaultmesh_root)
self.events_path = str(root / self.events_path)
self.root_path = str(root / self.root_path)
def _ensure_dirs(self) -> None:
"""Ensure parent directories exist."""
os.makedirs(os.path.dirname(self.events_path), exist_ok=True)
os.makedirs(os.path.dirname(self.root_path), exist_ok=True)
def _now_iso(self) -> str:
"""Return current UTC timestamp in ISO format."""
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _vmhash(self, data: bytes) -> str:
"""VaultMesh hash with algorithm prefix (blake3 preferred)."""
if blake3 is not None:
return f"blake3:{blake3.blake3(data).hexdigest()}"
# Fallback for environments without blake3
import hashlib
return f"sha256:{hashlib.sha256(data).hexdigest()}"
def _hex_part(self, value: str) -> str:
"""Return hash hex component (strip optional algorithm prefix)."""
return value.split(":", 1)[-1]
def _compute_merkle_root(self, hashes: List[str]) -> str:
"""
Compute Merkle root over a list of VaultMesh hashes.
Simple in-process implementation. O(n log n) time.
TODO: Replace with incremental frontier for O(log n) updates.
"""
if not hashes:
return self._vmhash(b"empty")
if len(hashes) == 1:
return hashes[0]
level = hashes[:]
while len(level) > 1:
next_level: List[str] = []
for i in range(0, len(level), 2):
left = level[i]
right = level[i + 1] if i + 1 < len(level) else left
combined = (self._hex_part(left) + self._hex_part(right)).encode("utf-8")
next_level.append(self._vmhash(combined))
level = next_level
return level[0]
def _recompute_root(self) -> None:
"""
Re-scan the JSONL file, hash each line, and update ROOT.console.txt.
This is O(n) but simple and correct for Phase 1 (Nigredo).
TODO: Replace with shared receipts frontier for O(log n) updates.
"""
hashes: List[str] = []
count = 0
if os.path.exists(self.events_path):
with open(self.events_path, "rb") as f:
for raw_line in f:
line = raw_line.rstrip(b"\n")
if not line:
continue
hashes.append(self._vmhash(line))
count += 1
root = self._compute_merkle_root(hashes)
# Write updated root file
self._ensure_dirs()
with open(self.root_path, "w", encoding="utf-8") as f:
f.write(f"# VaultMesh Console Root\n")
f.write(f"engine_id={ENGINE_ID}\n")
f.write(f"merkle_root={root}\n")
f.write(f"events={count}\n")
f.write(f"updated_at={self._now_iso()}\n")
def emit(
self,
receipt_type: ReceiptType,
payload: Dict[str, Any],
*,
session_id: Optional[str] = None,
ts: Optional[str] = None,
) -> ConsoleReceipt:
"""
Emit a single Console receipt and update the engine root.
Args:
receipt_type: One of the defined ReceiptType values
payload: Domain-specific receipt data
session_id: Session identifier (required for non-genesis receipts)
ts: Optional timestamp override (ISO format)
Returns:
The emitted receipt record
Example:
emitter.emit(
"console_session_start",
{
"agent_type": "opencode",
"model_id": "claude-opus-4-5",
"caller": "did:vm:human:karol",
"project_path": "/root/work/vaultmesh"
},
session_id="session-1765123456",
)
"""
self._ensure_dirs()
record: ConsoleReceipt = {
"ts": ts or self._now_iso(),
"engine_id": ENGINE_ID,
"type": receipt_type,
"session_id": session_id,
"payload": payload,
}
# Append to scroll (compact JSON, one line)
line = json.dumps(record, separators=(",", ":"))
with open(self.events_path, "a", encoding="utf-8") as f:
f.write(line + "\n")
# Update Merkle root
self._recompute_root()
return record
def get_root_info(self) -> Dict[str, Any]:
"""Read and parse the current ROOT.console.txt file."""
if not os.path.exists(self.root_path):
return {
"engine_id": ENGINE_ID,
"merkle_root": self._vmhash(b"empty"),
"events": 0,
"updated_at": None,
}
info = {}
with open(self.root_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line.startswith("#") or not line:
continue
if "=" in line:
key, value = line.split("=", 1)
if key == "events":
info[key] = int(value)
else:
info[key] = value
return info
# Convenience singleton for simple use
_default_emitter: Optional[ConsoleReceiptEmitter] = None
def get_emitter(vaultmesh_root: Optional[str] = None) -> ConsoleReceiptEmitter:
"""Get or create the default emitter singleton."""
global _default_emitter
if _default_emitter is None:
_default_emitter = ConsoleReceiptEmitter(vaultmesh_root=vaultmesh_root)
return _default_emitter
def emit_console_receipt(
receipt_type: ReceiptType,
payload: Dict[str, Any],
*,
session_id: Optional[str] = None,
ts: Optional[str] = None,
vaultmesh_root: Optional[str] = None,
) -> ConsoleReceipt:
"""
Emit a Console receipt using the default emitter.
Convenience function that uses a singleton emitter instance.
Args:
receipt_type: One of the defined ReceiptType values
payload: Domain-specific receipt data
session_id: Session identifier (required for non-genesis receipts)
ts: Optional timestamp override (ISO format)
vaultmesh_root: Optional VaultMesh repo root path
Returns:
The emitted receipt record
"""
emitter = get_emitter(vaultmesh_root)
return emitter.emit(
receipt_type=receipt_type,
payload=payload,
session_id=session_id,
ts=ts,
)