Merge pull request #2 from Outer-Void/codex/construct-conscious-agent-core-module
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .github/workflows/ci.yml +25 -0
- .github/workflows/docs.yml +24 -0
- .github/workflows/release.yml +25 -0
- README.md +11 -0
- blux_ca/__init__.py +15 -0
- blux_ca/adapters/__init__.py +16 -0
- blux_ca/adapters/doctrine.py +18 -0
- blux_ca/adapters/guard.py +16 -0
- blux_ca/adapters/lite.py +27 -0
- blux_ca/adapters/quantum.py +17 -0
- blux_ca/adapters/reg.py +23 -0
- blux_ca/api/__init__.py +6 -0
- blux_ca/api/schemas.py +22 -0
- blux_ca/api/service.py +44 -0
- blux_ca/cli.py +85 -0
- blux_ca/config.py +57 -0
- blux_ca/core/__init__.py +26 -0
- blux_ca/core/audit.py +44 -0
- blux_ca/core/constitution.py +48 -0
- blux_ca/core/discernment.py +34 -0
- blux_ca/core/intervention.py +35 -0
- blux_ca/core/perception.py +35 -0
- blux_ca/core/reflection.py +33 -0
- blux_ca/telemetry.py +37 -0
- docs/ARCHITECTURE.md +3 -0
- docs/CONFIGURATION.md +3 -0
- docs/CONSTITUTION.md +3 -0
- docs/DISCERNMENT.md +3 -0
- docs/ETHICS_ENGINE.md +3 -0
- docs/INSTALL.md +3 -0
- docs/INTEGRATIONS.md +3 -0
- docs/INTERVENTIONS.md +3 -0
- docs/OPERATIONS.md +3 -0
- docs/PRIVACY.md +3 -0
- docs/ROADMAP.md +3 -0
- docs/SECURITY.md +3 -0
- docs/TROUBLESHOOTING.md +3 -0
- docs/VISION.md +3 -0
- docs/index.md +3 -0
- mkdocs.yml +22 -0
- pyproject.toml +27 -0
- scripts/export_audit_json.py +22 -0
- scripts/gen_filetree.py +20 -0
- scripts/run_reflection_test.py +16 -0
- scripts/update_readme_filetree.py +21 -0
- scripts/validate_constitution.py +25 -0
- tests/ca/test_audit.py +11 -0
- tests/ca/test_constitution.py +15 -0
- tests/ca/test_discernment.py +13 -0
- tests/ca/test_interventions.py +12 -0
.github/workflows/ci.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [main]
|
| 6 |
+
pull_request:
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
build:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
steps:
|
| 12 |
+
- uses: actions/checkout@v4
|
| 13 |
+
- uses: actions/setup-python@v5
|
| 14 |
+
with:
|
| 15 |
+
python-version: '3.11'
|
| 16 |
+
- name: Install dependencies
|
| 17 |
+
run: |
|
| 18 |
+
python -m pip install --upgrade pip
|
| 19 |
+
pip install -e .[dev]
|
| 20 |
+
- name: Lint
|
| 21 |
+
run: |
|
| 22 |
+
ruff blux_ca
|
| 23 |
+
mypy blux_ca
|
| 24 |
+
- name: Tests
|
| 25 |
+
run: pytest tests/ca
|
.github/workflows/docs.yml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Docs
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [main]
|
| 6 |
+
pull_request:
|
| 7 |
+
paths:
|
| 8 |
+
- 'docs/**'
|
| 9 |
+
- 'mkdocs.yml'
|
| 10 |
+
|
| 11 |
+
jobs:
|
| 12 |
+
build:
|
| 13 |
+
runs-on: ubuntu-latest
|
| 14 |
+
steps:
|
| 15 |
+
- uses: actions/checkout@v4
|
| 16 |
+
- uses: actions/setup-python@v5
|
| 17 |
+
with:
|
| 18 |
+
python-version: '3.11'
|
| 19 |
+
- name: Install mkdocs
|
| 20 |
+
run: |
|
| 21 |
+
python -m pip install --upgrade pip
|
| 22 |
+
pip install mkdocs mkdocs-material
|
| 23 |
+
- name: Build site
|
| 24 |
+
run: mkdocs build --strict
|
.github/workflows/release.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Release
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
tags:
|
| 6 |
+
- 'v*'
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
release:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
steps:
|
| 12 |
+
- uses: actions/checkout@v4
|
| 13 |
+
- uses: actions/setup-python@v5
|
| 14 |
+
with:
|
| 15 |
+
python-version: '3.11'
|
| 16 |
+
- name: Build package
|
| 17 |
+
run: |
|
| 18 |
+
python -m pip install --upgrade pip
|
| 19 |
+
pip install build
|
| 20 |
+
python -m build
|
| 21 |
+
- name: Upload artifacts
|
| 22 |
+
uses: actions/upload-artifact@v4
|
| 23 |
+
with:
|
| 24 |
+
name: dist
|
| 25 |
+
path: dist
|
README.md
CHANGED
|
@@ -133,3 +133,14 @@ Document any new evaluators, adaptors, or agents in README.
|
|
| 133 |
# License
|
| 134 |
|
| 135 |
MIT License
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
# License
|
| 134 |
|
| 135 |
MIT License
|
| 136 |
+
|
| 137 |
+
## Conscious Agent Core (Enterprise)
|
| 138 |
+
|
| 139 |
+
The `blux_ca` package implements the enterprise-grade conscious agent kernel. It ships with:
|
| 140 |
+
|
| 141 |
+
- Perception, reflection, discernment, constitution, intervention, and audit layers.
|
| 142 |
+
- FastAPI microservice factory under `blux_ca.api.service`.
|
| 143 |
+
- Typer CLI exposed via `blux_ca.cli:get_app`.
|
| 144 |
+
- Integration adapters for Doctrine, Guard, Lite orchestrator, and Quantum CLI.
|
| 145 |
+
- Documentation suite served with MkDocs Material.
|
| 146 |
+
- Scripts for generating file trees, exporting audits, and validating doctrine scenarios.
|
blux_ca/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""BLUX-cA package root for the Conscious Agent core."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from importlib import import_module
|
| 6 |
+
from typing import Any
|
| 7 |
+
|
| 8 |
+
__all__ = ["get_app"]
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def get_app() -> Any:
|
| 12 |
+
"""Return the Typer application without importing Typer at module load time."""
|
| 13 |
+
|
| 14 |
+
cli = import_module("blux_ca.cli")
|
| 15 |
+
return cli.get_app()
|
blux_ca/adapters/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Integration adapters for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from .doctrine import DoctrineAdapter
|
| 4 |
+
from .guard import GuardAdapter
|
| 5 |
+
from .lite import LiteAdapter
|
| 6 |
+
from .quantum import QuantumAdapter
|
| 7 |
+
from .reg import RegistryValidator, RegistrationResult
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
"DoctrineAdapter",
|
| 11 |
+
"GuardAdapter",
|
| 12 |
+
"LiteAdapter",
|
| 13 |
+
"QuantumAdapter",
|
| 14 |
+
"RegistryValidator",
|
| 15 |
+
"RegistrationResult",
|
| 16 |
+
]
|
blux_ca/adapters/doctrine.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adapter for interacting with the BLUX Doctrine API."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Dict
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class DoctrineAdapter:
|
| 9 |
+
"""Placeholder adapter that would call the doctrine policy API."""
|
| 10 |
+
|
| 11 |
+
def __init__(self, endpoint: str = "https://doctrine.blux.local") -> None:
|
| 12 |
+
self.endpoint = endpoint
|
| 13 |
+
|
| 14 |
+
def fetch_policy(self) -> Dict[str, str]:
|
| 15 |
+
return {"law.integrity": "Integrity over everything."}
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
__all__ = ["DoctrineAdapter"]
|
blux_ca/adapters/guard.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adapter connecting the cA with BLUX-Guard."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Dict
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class GuardAdapter:
|
| 9 |
+
"""Minimal guard interface for policy enforcement."""
|
| 10 |
+
|
| 11 |
+
def notify(self, verdict: Dict[str, str]) -> None:
|
| 12 |
+
# In production this would forward verdicts to BLUX-Guard.
|
| 13 |
+
_ = verdict
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
__all__ = ["GuardAdapter"]
|
blux_ca/adapters/lite.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adapter bridging BLUX-Lite orchestrator."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict
|
| 6 |
+
|
| 7 |
+
from ..core.constitution import ConstitutionEngine
|
| 8 |
+
from ..core.discernment import DiscernmentCompass
|
| 9 |
+
from ..core.reflection import ReflectionEngine
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class LiteAdapter:
|
| 13 |
+
"""Provides a high-level evaluate entrypoint used by BLUX-Lite."""
|
| 14 |
+
|
| 15 |
+
def __init__(self) -> None:
|
| 16 |
+
self.reflection = ReflectionEngine()
|
| 17 |
+
self.compass = DiscernmentCompass()
|
| 18 |
+
self.constitution = ConstitutionEngine()
|
| 19 |
+
|
| 20 |
+
def evaluate(self, text: str) -> Dict[str, Any]:
|
| 21 |
+
intent = self.compass.classify(text)
|
| 22 |
+
insight = self.reflection.reflect(text)
|
| 23 |
+
verdict = self.constitution.evaluate(insights=insight.chain, intent=intent.intent.value)
|
| 24 |
+
return verdict.__dict__
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
__all__ = ["LiteAdapter"]
|
blux_ca/adapters/quantum.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Adapter for `bluxq ca` commands."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict
|
| 6 |
+
|
| 7 |
+
from ..cli import get_app
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class QuantumAdapter:
|
| 11 |
+
"""Provides entrypoint metadata for the BLUX quantum CLI."""
|
| 12 |
+
|
| 13 |
+
def load(self) -> Dict[str, Any]:
|
| 14 |
+
return {"name": "ca", "app": get_app()}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
__all__ = ["QuantumAdapter"]
|
blux_ca/adapters/reg.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Registration helper for validating keys and capabilities."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@dataclass
|
| 9 |
+
class RegistrationResult:
|
| 10 |
+
valid: bool
|
| 11 |
+
reason: str
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class RegistryValidator:
|
| 15 |
+
"""Performs simple capability validation."""
|
| 16 |
+
|
| 17 |
+
def validate(self, key: str) -> RegistrationResult:
|
| 18 |
+
if key.startswith("BLUX-"):
|
| 19 |
+
return RegistrationResult(True, "Key accepted.")
|
| 20 |
+
return RegistrationResult(False, "Key must start with 'BLUX-'.")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
__all__ = ["RegistryValidator", "RegistrationResult"]
|
blux_ca/api/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""API helpers for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from .schemas import ReflectRequest, ReflectResponse, VerdictResponse
|
| 4 |
+
from .service import ConsciousAgentService
|
| 5 |
+
|
| 6 |
+
__all__ = ["ConsciousAgentService", "ReflectRequest", "ReflectResponse", "VerdictResponse"]
|
blux_ca/api/schemas.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pydantic schemas for the BLUX-cA API."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from pydantic import BaseModel, Field
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class ReflectRequest(BaseModel):
|
| 9 |
+
text: str = Field(..., description="User supplied text for reflection")
|
| 10 |
+
depth: int = Field(3, ge=1, le=10)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class ReflectResponse(BaseModel):
|
| 14 |
+
summary: str
|
| 15 |
+
chain: list[str]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class VerdictResponse(BaseModel):
|
| 19 |
+
decision: str
|
| 20 |
+
score: float
|
| 21 |
+
doctrine_refs: list[str]
|
| 22 |
+
reason: str
|
blux_ca/api/service.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FastAPI service for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from fastapi import FastAPI
|
| 6 |
+
|
| 7 |
+
from ..core.constitution import ConstitutionEngine
|
| 8 |
+
from ..core.discernment import DiscernmentCompass
|
| 9 |
+
from ..core.perception import PerceptionLayer
|
| 10 |
+
from ..core.reflection import ReflectionEngine
|
| 11 |
+
from .schemas import ReflectRequest, ReflectResponse, VerdictResponse
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class ConsciousAgentService:
|
| 15 |
+
"""Factory for FastAPI application exposing the cA capabilities."""
|
| 16 |
+
|
| 17 |
+
def __init__(self) -> None:
|
| 18 |
+
self.perception = PerceptionLayer()
|
| 19 |
+
self.reflection = ReflectionEngine()
|
| 20 |
+
self.compass = DiscernmentCompass()
|
| 21 |
+
self.constitution = ConstitutionEngine()
|
| 22 |
+
|
| 23 |
+
def create_app(self) -> FastAPI:
|
| 24 |
+
app = FastAPI(title="BLUX-cA", version="0.1.0")
|
| 25 |
+
|
| 26 |
+
@app.post("/reflect", response_model=ReflectResponse)
|
| 27 |
+
def reflect(payload: ReflectRequest) -> ReflectResponse:
|
| 28 |
+
observed = self.perception.observe(payload.text)
|
| 29 |
+
insight = self.reflection.reflect(observed.text, seeds=["Initial observation"])
|
| 30 |
+
return ReflectResponse(summary=insight.summary, chain=insight.chain)
|
| 31 |
+
|
| 32 |
+
@app.post("/verdict", response_model=VerdictResponse)
|
| 33 |
+
def verdict(payload: ReflectRequest) -> VerdictResponse:
|
| 34 |
+
intent = self.compass.classify(payload.text)
|
| 35 |
+
insight = self.reflection.reflect(payload.text, seeds=["Policy alignment"])
|
| 36 |
+
decision = self.constitution.evaluate(
|
| 37 |
+
insights=insight.chain, intent=intent.intent.value
|
| 38 |
+
)
|
| 39 |
+
return VerdictResponse(**decision.__dict__)
|
| 40 |
+
|
| 41 |
+
return app
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
__all__ = ["ConsciousAgentService"]
|
blux_ca/cli.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Typer CLI for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import hashlib
|
| 6 |
+
import json
|
| 7 |
+
from typing import Optional
|
| 8 |
+
|
| 9 |
+
import typer
|
| 10 |
+
|
| 11 |
+
from .config import load_config
|
| 12 |
+
from .core.audit import AuditLog
|
| 13 |
+
from .core.constitution import ConstitutionEngine
|
| 14 |
+
from .core.discernment import DiscernmentCompass
|
| 15 |
+
from .core.perception import PerceptionLayer
|
| 16 |
+
from .core.reflection import ReflectionEngine
|
| 17 |
+
|
| 18 |
+
app = typer.Typer(help="BLUX-cA conscious agent core")
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _hash_text(text: str) -> str:
|
| 22 |
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@app.command()
|
| 26 |
+
def reflect(text: str, depth: int = typer.Option(3, help="Number of why-chain iterations.")) -> None:
|
| 27 |
+
perception = PerceptionLayer()
|
| 28 |
+
reflection = ReflectionEngine(depth=depth)
|
| 29 |
+
entry = perception.observe(text)
|
| 30 |
+
insight = reflection.reflect(entry.text)
|
| 31 |
+
typer.echo(json.dumps(insight.__dict__, indent=2, ensure_ascii=False))
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@app.command()
|
| 35 |
+
def explain(last: bool = typer.Option(False, help="Explain the most recent audit entry.")) -> None:
|
| 36 |
+
if not last:
|
| 37 |
+
typer.echo("Provide --last to view the latest explanation.")
|
| 38 |
+
raise typer.Exit(code=1)
|
| 39 |
+
audit = AuditLog()
|
| 40 |
+
if not audit.path.exists():
|
| 41 |
+
typer.echo("No audit history available.")
|
| 42 |
+
raise typer.Exit(code=1)
|
| 43 |
+
lines = audit.path.read_text(encoding="utf-8").strip().splitlines()
|
| 44 |
+
if not lines:
|
| 45 |
+
typer.echo("Audit log empty.")
|
| 46 |
+
raise typer.Exit(code=1)
|
| 47 |
+
typer.echo(lines[-1])
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
@app.command()
|
| 51 |
+
def audit_export(output: Optional[str] = typer.Option(None, help="Export path.")) -> None:
|
| 52 |
+
audit = AuditLog()
|
| 53 |
+
if not audit.path.exists():
|
| 54 |
+
typer.echo("No audit history available.")
|
| 55 |
+
return
|
| 56 |
+
target = output or "audit_export.jsonl"
|
| 57 |
+
typer.echo(f"Exporting audit log to {target}")
|
| 58 |
+
typer.echo(audit.path.read_text(encoding="utf-8"))
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
@app.command()
|
| 62 |
+
def doctrine(text: str) -> None:
|
| 63 |
+
config = load_config()
|
| 64 |
+
compass = DiscernmentCompass()
|
| 65 |
+
constitution = ConstitutionEngine(mode=config.get("mode", "strict"))
|
| 66 |
+
insights = [text]
|
| 67 |
+
decision = constitution.evaluate(insights=insights, intent=compass.classify(text).intent.value)
|
| 68 |
+
audit = AuditLog()
|
| 69 |
+
record = audit.create_record(
|
| 70 |
+
input_hash=_hash_text(text),
|
| 71 |
+
verdict=decision.decision,
|
| 72 |
+
doctrine_refs=decision.doctrine_refs,
|
| 73 |
+
rationale=decision.reason,
|
| 74 |
+
)
|
| 75 |
+
audit.append(record)
|
| 76 |
+
typer.echo(json.dumps(decision.__dict__, indent=2, ensure_ascii=False))
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def get_app() -> typer.Typer:
|
| 80 |
+
"""Return the Typer application for integration with ``bluxq``."""
|
| 81 |
+
|
| 82 |
+
return app
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
__all__ = ["get_app", "app"]
|
blux_ca/config.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configuration loader for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Any, Dict
|
| 9 |
+
|
| 10 |
+
import yaml
|
| 11 |
+
|
| 12 |
+
DEFAULT_CONFIG_FILENAMES = ("config.yaml", "config.yml")
|
| 13 |
+
CONFIG_ENV_PREFIX = "BLUX_CA_"
|
| 14 |
+
USER_CONFIG_DIR = Path.home() / ".config" / "blux-ca"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _load_yaml(path: Path) -> Dict[str, Any]:
|
| 18 |
+
if not path.exists():
|
| 19 |
+
return {}
|
| 20 |
+
with path.open("r", encoding="utf-8") as handle:
|
| 21 |
+
return yaml.safe_load(handle) or {}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _load_env() -> Dict[str, Any]:
|
| 25 |
+
config: Dict[str, Any] = {}
|
| 26 |
+
for key, value in os.environ.items():
|
| 27 |
+
if not key.startswith(CONFIG_ENV_PREFIX):
|
| 28 |
+
continue
|
| 29 |
+
normalized = key[len(CONFIG_ENV_PREFIX) :].lower()
|
| 30 |
+
try:
|
| 31 |
+
config[normalized] = json.loads(value)
|
| 32 |
+
except json.JSONDecodeError:
|
| 33 |
+
config[normalized] = value
|
| 34 |
+
return config
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def load_config(cwd: Path | None = None) -> Dict[str, Any]:
|
| 38 |
+
"""Load configuration from environment and YAML files.
|
| 39 |
+
|
| 40 |
+
Parameters
|
| 41 |
+
----------
|
| 42 |
+
cwd:
|
| 43 |
+
Optional working directory to search for configuration files.
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
cwd = cwd or Path.cwd()
|
| 47 |
+
config: Dict[str, Any] = {}
|
| 48 |
+
|
| 49 |
+
for filename in DEFAULT_CONFIG_FILENAMES:
|
| 50 |
+
config.update(_load_yaml(USER_CONFIG_DIR / filename))
|
| 51 |
+
config.update(_load_yaml(cwd / filename))
|
| 52 |
+
|
| 53 |
+
config.update(_load_env())
|
| 54 |
+
return config
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
__all__ = ["load_config"]
|
blux_ca/core/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Core modules for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from .audit import AuditLog, AuditRecord
|
| 4 |
+
from .constitution import ConstitutionEngine, DoctrineVerdict
|
| 5 |
+
from .discernment import DiscernmentCompass, DiscernmentDecision, IntentType
|
| 6 |
+
from .intervention import compassionate_edge, layered_truth, light_shift, mirror
|
| 7 |
+
from .perception import PerceptionInput, PerceptionLayer
|
| 8 |
+
from .reflection import ReflectionEngine, ReflectionInsight
|
| 9 |
+
|
| 10 |
+
__all__ = [
|
| 11 |
+
"AuditLog",
|
| 12 |
+
"AuditRecord",
|
| 13 |
+
"ConstitutionEngine",
|
| 14 |
+
"DoctrineVerdict",
|
| 15 |
+
"DiscernmentCompass",
|
| 16 |
+
"DiscernmentDecision",
|
| 17 |
+
"IntentType",
|
| 18 |
+
"compassionate_edge",
|
| 19 |
+
"layered_truth",
|
| 20 |
+
"light_shift",
|
| 21 |
+
"mirror",
|
| 22 |
+
"PerceptionInput",
|
| 23 |
+
"PerceptionLayer",
|
| 24 |
+
"ReflectionEngine",
|
| 25 |
+
"ReflectionInsight",
|
| 26 |
+
]
|
blux_ca/core/audit.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Append-only audit log for BLUX-cA decisions."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
from dataclasses import dataclass, asdict
|
| 7 |
+
from datetime import datetime, timezone
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Iterable, List
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class AuditRecord:
|
| 14 |
+
timestamp: str
|
| 15 |
+
input_hash: str
|
| 16 |
+
verdict: str
|
| 17 |
+
doctrine_refs: List[str]
|
| 18 |
+
rationale: str
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class AuditLog:
|
| 22 |
+
"""Append-only JSONL audit log."""
|
| 23 |
+
|
| 24 |
+
def __init__(self, path: Path | None = None) -> None:
|
| 25 |
+
self.path = path or Path.home() / ".config" / "blux-ca" / "audit" / "decisions.jsonl"
|
| 26 |
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
| 27 |
+
|
| 28 |
+
def append(self, record: AuditRecord) -> None:
|
| 29 |
+
with self.path.open("a", encoding="utf-8") as handle:
|
| 30 |
+
handle.write(json.dumps(asdict(record), ensure_ascii=False) + "\n")
|
| 31 |
+
|
| 32 |
+
def create_record(
|
| 33 |
+
self, *, input_hash: str, verdict: str, doctrine_refs: Iterable[str], rationale: str
|
| 34 |
+
) -> AuditRecord:
|
| 35 |
+
return AuditRecord(
|
| 36 |
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
| 37 |
+
input_hash=input_hash,
|
| 38 |
+
verdict=verdict,
|
| 39 |
+
doctrine_refs=list(doctrine_refs),
|
| 40 |
+
rationale=rationale,
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
__all__ = ["AuditLog", "AuditRecord"]
|
blux_ca/core/constitution.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Constitution engine enforcing BLUX doctrine pillars."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from typing import Iterable, List, Sequence
|
| 7 |
+
|
| 8 |
+
PILLARS = ("integrity", "approval", "truth", "comfort")
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@dataclass
|
| 12 |
+
class DoctrineVerdict:
|
| 13 |
+
decision: str
|
| 14 |
+
score: float
|
| 15 |
+
doctrine_refs: List[str]
|
| 16 |
+
reason: str
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class ConstitutionEngine:
|
| 20 |
+
"""Simple interpreter that enforces doctrine priorities."""
|
| 21 |
+
|
| 22 |
+
def __init__(self, *, mode: str = "strict") -> None:
|
| 23 |
+
if mode not in {"strict", "soft", "mirror"}:
|
| 24 |
+
raise ValueError(f"Unsupported mode: {mode}")
|
| 25 |
+
self.mode = mode
|
| 26 |
+
|
| 27 |
+
def evaluate(self, *, insights: Sequence[str], intent: str) -> DoctrineVerdict:
|
| 28 |
+
score = min(1.0, 0.25 * len(insights))
|
| 29 |
+
doctrine_refs = [f"law.{pillar}" for pillar in PILLARS]
|
| 30 |
+
if intent == "harm":
|
| 31 |
+
decision = "deny"
|
| 32 |
+
reason = "Integrity over comfort: harm intent denied."
|
| 33 |
+
score = 0.0
|
| 34 |
+
elif intent == "indulger":
|
| 35 |
+
decision = "reflect"
|
| 36 |
+
reason = "Encourage accountability before approval."
|
| 37 |
+
else:
|
| 38 |
+
decision = "allow"
|
| 39 |
+
reason = "Support struggle with guided reflection."
|
| 40 |
+
return DoctrineVerdict(
|
| 41 |
+
decision=decision,
|
| 42 |
+
score=score,
|
| 43 |
+
doctrine_refs=doctrine_refs,
|
| 44 |
+
reason=reason,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
__all__ = ["ConstitutionEngine", "DoctrineVerdict"]
|
blux_ca/core/discernment.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Discernment compass differentiating user intent."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from enum import Enum
|
| 7 |
+
from typing import Dict
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class IntentType(str, Enum):
|
| 11 |
+
STRUGGLER = "struggler"
|
| 12 |
+
INDULGER = "indulger"
|
| 13 |
+
HARM = "harm"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class DiscernmentDecision:
|
| 18 |
+
intent: IntentType
|
| 19 |
+
rationale: str
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class DiscernmentCompass:
|
| 23 |
+
"""Classifies intent using simple heuristics."""
|
| 24 |
+
|
| 25 |
+
def classify(self, text: str) -> DiscernmentDecision:
|
| 26 |
+
lowered = text.lower()
|
| 27 |
+
if any(word in lowered for word in ("hurt", "harm", "kill")):
|
| 28 |
+
return DiscernmentDecision(IntentType.HARM, "Detected explicit harm intent.")
|
| 29 |
+
if any(word in lowered for word in ("enjoy", "love", "want", "indulge")):
|
| 30 |
+
return DiscernmentDecision(IntentType.INDULGER, "Language emphasises indulgence.")
|
| 31 |
+
return DiscernmentDecision(IntentType.STRUGGLER, "Defaulting to supportive framing.")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
__all__ = ["DiscernmentCompass", "DiscernmentDecision", "IntentType"]
|
blux_ca/core/intervention.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Intervention strategies for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from typing import Dict
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def mirror(message: str) -> Dict[str, str]:
|
| 9 |
+
return {"strategy": "mirror", "response": f"I hear that {message}"}
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def light_shift(message: str) -> Dict[str, str]:
|
| 13 |
+
return {"strategy": "light_shift", "response": f"What if we reframed: {message}"}
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def compassionate_edge(boundary: str) -> Dict[str, str]:
|
| 17 |
+
return {
|
| 18 |
+
"strategy": "compassionate_edge",
|
| 19 |
+
"response": f"I care about you, so I must set this boundary: {boundary}",
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def layered_truth(statement: str) -> Dict[str, str]:
|
| 24 |
+
return {
|
| 25 |
+
"strategy": "layered_truth",
|
| 26 |
+
"response": f"Here is the layered truth: {statement}",
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
__all__ = [
|
| 31 |
+
"mirror",
|
| 32 |
+
"light_shift",
|
| 33 |
+
"compassionate_edge",
|
| 34 |
+
"layered_truth",
|
| 35 |
+
]
|
blux_ca/core/perception.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Perception layer for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import hashlib
|
| 6 |
+
from dataclasses import dataclass
|
| 7 |
+
from typing import Dict, Iterable, List
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@dataclass
|
| 11 |
+
class PerceptionInput:
|
| 12 |
+
"""Normalized representation of an inbound stimulus."""
|
| 13 |
+
|
| 14 |
+
text: str
|
| 15 |
+
tags: List[str]
|
| 16 |
+
fingerprint: str
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class PerceptionLayer:
|
| 20 |
+
"""Perception layer that normalizes raw inputs into a structured payload."""
|
| 21 |
+
|
| 22 |
+
def __init__(self, *, default_tags: Iterable[str] | None = None) -> None:
|
| 23 |
+
self._default_tags = list(default_tags or [])
|
| 24 |
+
|
| 25 |
+
@staticmethod
|
| 26 |
+
def _fingerprint(text: str) -> str:
|
| 27 |
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
| 28 |
+
|
| 29 |
+
def observe(self, text: str, *, tags: Iterable[str] | None = None) -> PerceptionInput:
|
| 30 |
+
normalized_tags = sorted(set(self._default_tags + list(tags or [])))
|
| 31 |
+
fingerprint = self._fingerprint(text)
|
| 32 |
+
return PerceptionInput(text=text.strip(), tags=normalized_tags, fingerprint=fingerprint)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
__all__ = ["PerceptionLayer", "PerceptionInput"]
|
blux_ca/core/reflection.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Reflection layer producing why-chains."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from typing import Iterable, List
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@dataclass
|
| 10 |
+
class ReflectionInsight:
|
| 11 |
+
"""Structured representation of a reflection cycle."""
|
| 12 |
+
|
| 13 |
+
summary: str
|
| 14 |
+
chain: List[str]
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class ReflectionEngine:
|
| 18 |
+
"""Produces recursive why-chains to explain a decision."""
|
| 19 |
+
|
| 20 |
+
def __init__(self, *, depth: int = 3) -> None:
|
| 21 |
+
self.depth = max(1, depth)
|
| 22 |
+
|
| 23 |
+
def reflect(self, prompt: str, *, seeds: Iterable[str] | None = None) -> ReflectionInsight:
|
| 24 |
+
chain = list(seeds or [])
|
| 25 |
+
current_reason = prompt.strip()
|
| 26 |
+
for _ in range(self.depth):
|
| 27 |
+
chain.append(current_reason)
|
| 28 |
+
current_reason = f"Because {current_reason.lower()}"
|
| 29 |
+
summary = chain[-1] if chain else "No reflection available."
|
| 30 |
+
return ReflectionInsight(summary=summary, chain=chain)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
__all__ = ["ReflectionEngine", "ReflectionInsight"]
|
blux_ca/telemetry.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Lightweight telemetry helper for BLUX-cA."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
from datetime import datetime, timezone
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any, Dict
|
| 10 |
+
|
| 11 |
+
TELEMETRY_ENV = "BLUX_CA_TELEMETRY"
|
| 12 |
+
DEFAULT_TELEMETRY_PATH = Path.home() / ".config" / "blux-ca" / "telemetry.jsonl"
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def _is_enabled() -> bool:
|
| 16 |
+
return os.environ.get(TELEMETRY_ENV, "on").lower() not in {"0", "off", "false"}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def emit(event: str, payload: Dict[str, Any] | None = None) -> None:
|
| 20 |
+
"""Record a telemetry event when telemetry is enabled."""
|
| 21 |
+
|
| 22 |
+
if not _is_enabled():
|
| 23 |
+
return
|
| 24 |
+
|
| 25 |
+
record = {
|
| 26 |
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 27 |
+
"event": event,
|
| 28 |
+
"payload": payload or {},
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
path = DEFAULT_TELEMETRY_PATH
|
| 32 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 33 |
+
with path.open("a", encoding="utf-8") as handle:
|
| 34 |
+
handle.write(json.dumps(record, ensure_ascii=False) + "\n")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
__all__ = ["emit"]
|
docs/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ARCHITECTURE
|
| 2 |
+
|
| 3 |
+
This document outlines the architecture aspects of the BLUX-cA module.
|
docs/CONFIGURATION.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CONFIGURATION
|
| 2 |
+
|
| 3 |
+
This document outlines the configuration aspects of the BLUX-cA module.
|
docs/CONSTITUTION.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CONSTITUTION
|
| 2 |
+
|
| 3 |
+
This document outlines the constitution aspects of the BLUX-cA module.
|
docs/DISCERNMENT.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DISCERNMENT
|
| 2 |
+
|
| 3 |
+
This document outlines the discernment aspects of the BLUX-cA module.
|
docs/ETHICS_ENGINE.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ETHICS ENGINE
|
| 2 |
+
|
| 3 |
+
This document outlines the ethics_engine aspects of the BLUX-cA module.
|
docs/INSTALL.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# INSTALL
|
| 2 |
+
|
| 3 |
+
This document outlines the install aspects of the BLUX-cA module.
|
docs/INTEGRATIONS.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# INTEGRATIONS
|
| 2 |
+
|
| 3 |
+
This document outlines the integrations aspects of the BLUX-cA module.
|
docs/INTERVENTIONS.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# INTERVENTIONS
|
| 2 |
+
|
| 3 |
+
This document outlines the interventions aspects of the BLUX-cA module.
|
docs/OPERATIONS.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OPERATIONS
|
| 2 |
+
|
| 3 |
+
This document outlines the operations aspects of the BLUX-cA module.
|
docs/PRIVACY.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PRIVACY
|
| 2 |
+
|
| 3 |
+
This document outlines the privacy aspects of the BLUX-cA module.
|
docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROADMAP
|
| 2 |
+
|
| 3 |
+
This document outlines the roadmap aspects of the BLUX-cA module.
|
docs/SECURITY.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SECURITY
|
| 2 |
+
|
| 3 |
+
This document outlines the security aspects of the BLUX-cA module.
|
docs/TROUBLESHOOTING.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TROUBLESHOOTING
|
| 2 |
+
|
| 3 |
+
This document outlines the troubleshooting aspects of the BLUX-cA module.
|
docs/VISION.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# VISION
|
| 2 |
+
|
| 3 |
+
This document outlines the vision aspects of the BLUX-cA module.
|
docs/index.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# BLUX-cA Conscious Agent Core
|
| 2 |
+
|
| 3 |
+
Welcome to the BLUX-cA documentation set.
|
mkdocs.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
site_name: BLUX-cA
|
| 2 |
+
site_description: Enterprise Conscious Agent Core
|
| 3 |
+
nav:
|
| 4 |
+
- Home: index.md
|
| 5 |
+
- Vision: VISION.md
|
| 6 |
+
- Architecture: ARCHITECTURE.md
|
| 7 |
+
- Constitution: CONSTITUTION.md
|
| 8 |
+
- Ethics Engine: ETHICS_ENGINE.md
|
| 9 |
+
- Discernment: DISCERNMENT.md
|
| 10 |
+
- Interventions: INTERVENTIONS.md
|
| 11 |
+
- Integrations: INTEGRATIONS.md
|
| 12 |
+
- Install: INSTALL.md
|
| 13 |
+
- Operations: OPERATIONS.md
|
| 14 |
+
- Security: SECURITY.md
|
| 15 |
+
- Privacy: PRIVACY.md
|
| 16 |
+
- Configuration: CONFIGURATION.md
|
| 17 |
+
- Troubleshooting: TROUBLESHOOTING.md
|
| 18 |
+
- Roadmap: ROADMAP.md
|
| 19 |
+
theme:
|
| 20 |
+
name: material
|
| 21 |
+
features:
|
| 22 |
+
- content.code.copy
|
pyproject.toml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=64", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "blux-ca"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "BLUX Conscious Agent core"
|
| 9 |
+
authors = [{name = "BLUX", email = "ca@blux.ai"}]
|
| 10 |
+
readme = "README.md"
|
| 11 |
+
requires-python = ">=3.10"
|
| 12 |
+
dependencies = [
|
| 13 |
+
"typer[all]",
|
| 14 |
+
"fastapi",
|
| 15 |
+
"pydantic>=1.10,<2.0",
|
| 16 |
+
"PyYAML",
|
| 17 |
+
]
|
| 18 |
+
|
| 19 |
+
[project.optional-dependencies]
|
| 20 |
+
dev = ["pytest", "ruff", "mypy"]
|
| 21 |
+
|
| 22 |
+
[project.entry-points."blux.plugins"]
|
| 23 |
+
ca = "blux_ca.cli:get_app"
|
| 24 |
+
|
| 25 |
+
[tool.pytest.ini_options]
|
| 26 |
+
pythonpath = ["."]
|
| 27 |
+
addopts = "-q"
|
scripts/export_audit_json.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Export audit logs into a merged JSON document."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
from blux_ca.core.audit import AuditLog
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def export(output: Path = Path("audit_export.json")) -> None:
|
| 12 |
+
audit = AuditLog()
|
| 13 |
+
if not audit.path.exists():
|
| 14 |
+
print("No audit log available.")
|
| 15 |
+
return
|
| 16 |
+
lines = [json.loads(line) for line in audit.path.read_text(encoding="utf-8").splitlines() if line]
|
| 17 |
+
output.write_text(json.dumps(lines, indent=2, ensure_ascii=False), encoding="utf-8")
|
| 18 |
+
print(f"Exported {len(lines)} records to {output}")
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
if __name__ == "__main__":
|
| 22 |
+
export()
|
scripts/gen_filetree.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Generate repository file tree summary."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def generate(root: str = ".") -> str:
|
| 10 |
+
lines: list[str] = []
|
| 11 |
+
for current_root, dirs, files in os.walk(root):
|
| 12 |
+
level = Path(current_root).relative_to(root).parts
|
| 13 |
+
indent = " " * len(level)
|
| 14 |
+
for name in sorted(files):
|
| 15 |
+
lines.append(f"{indent}{name}")
|
| 16 |
+
return "\n".join(lines)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if __name__ == "__main__":
|
| 20 |
+
print(generate())
|
scripts/run_reflection_test.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Execute a simple reflection self-test."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from blux_ca.core.reflection import ReflectionEngine
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def run(prompt: str = "Why integrity matters?") -> None:
|
| 9 |
+
engine = ReflectionEngine()
|
| 10 |
+
insight = engine.reflect(prompt)
|
| 11 |
+
for idx, reason in enumerate(insight.chain, start=1):
|
| 12 |
+
print(f"{idx}. {reason}")
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
if __name__ == "__main__":
|
| 16 |
+
run()
|
scripts/update_readme_filetree.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Update README with generated file tree."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
from gen_filetree import generate
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
README_MARKER = "<!-- FILETREE -->"
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def update_readme(readme_path: Path = Path("README.md")) -> None:
|
| 14 |
+
content = readme_path.read_text(encoding="utf-8")
|
| 15 |
+
tree = generate()
|
| 16 |
+
snippet = f"{README_MARKER}\n\n````\n{tree}\n````"
|
| 17 |
+
readme_path.write_text(snippet, encoding="utf-8")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if __name__ == "__main__":
|
| 21 |
+
update_readme()
|
scripts/validate_constitution.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Validate constitution logic with sample cases."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from blux_ca.core.constitution import ConstitutionEngine
|
| 6 |
+
from blux_ca.core.discernment import DiscernmentCompass
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
CASES = {
|
| 10 |
+
"help": "I need help staying accountable.",
|
| 11 |
+
"indulger": "I love to indulge in bad habits.",
|
| 12 |
+
"harm": "I want to hurt them.",
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main() -> None:
|
| 17 |
+
compass = DiscernmentCompass()
|
| 18 |
+
engine = ConstitutionEngine()
|
| 19 |
+
for name, text in CASES.items():
|
| 20 |
+
decision = engine.evaluate(insights=[text], intent=compass.classify(text).intent.value)
|
| 21 |
+
print(name, decision)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
if __name__ == "__main__":
|
| 25 |
+
main()
|
tests/ca/test_audit.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
|
| 3 |
+
from blux_ca.core.audit import AuditLog
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_audit_appends(tmp_path: Path):
|
| 7 |
+
audit = AuditLog(path=tmp_path / "audit.jsonl")
|
| 8 |
+
record = audit.create_record(input_hash="abc", verdict="allow", doctrine_refs=["law"], rationale="ok")
|
| 9 |
+
audit.append(record)
|
| 10 |
+
contents = (tmp_path / "audit.jsonl").read_text(encoding="utf-8").strip()
|
| 11 |
+
assert "allow" in contents
|
tests/ca/test_constitution.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from blux_ca.core.constitution import ConstitutionEngine
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_constitution_denies_harm():
|
| 5 |
+
engine = ConstitutionEngine()
|
| 6 |
+
verdict = engine.evaluate(insights=["danger"], intent="harm")
|
| 7 |
+
assert verdict.decision == "deny"
|
| 8 |
+
assert verdict.score == 0.0
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def test_constitution_allows_struggler():
|
| 12 |
+
engine = ConstitutionEngine()
|
| 13 |
+
verdict = engine.evaluate(insights=["trying", "learning"], intent="struggler")
|
| 14 |
+
assert verdict.decision == "allow"
|
| 15 |
+
assert verdict.score > 0
|
tests/ca/test_discernment.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from blux_ca.core.discernment import DiscernmentCompass, IntentType
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_discernment_detects_harm():
|
| 5 |
+
compass = DiscernmentCompass()
|
| 6 |
+
decision = compass.classify("I want to hurt them")
|
| 7 |
+
assert decision.intent is IntentType.HARM
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def test_discernment_defaults_to_struggler():
|
| 11 |
+
compass = DiscernmentCompass()
|
| 12 |
+
decision = compass.classify("I am trying to do better")
|
| 13 |
+
assert decision.intent is IntentType.STRUGGLER
|
tests/ca/test_interventions.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from blux_ca.core import intervention
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_intervention_mirror():
|
| 5 |
+
result = intervention.mirror("you are valued")
|
| 6 |
+
assert result["strategy"] == "mirror"
|
| 7 |
+
assert "you are valued" in result["response"]
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def test_intervention_layered_truth():
|
| 11 |
+
result = intervention.layered_truth("integrity first")
|
| 12 |
+
assert result["strategy"] == "layered_truth"
|