@agentikos/omega-os 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/bin/omega-os.js +48 -0
- package/bootstrap/lib/common.sh +73 -0
- package/bootstrap/lib/steps.sh +153 -0
- package/bootstrap/manifest.example.yaml +45 -0
- package/docs/ACCOUNT-AND-BILLING.md +95 -0
- package/docs/ARCHITECTURE.md +225 -0
- package/docs/AUTONOMOUS-AGENTS.md +128 -0
- package/docs/ENGINE-SPEC.md +174 -0
- package/docs/INSTALL.md +106 -0
- package/docs/MCP-AND-PLUGINS.md +121 -0
- package/docs/RUNTIME-PLAN.md +63 -0
- package/install.sh +54 -0
- package/omega/Agentik_Coding/README.md +21 -0
- package/omega/Agentik_Engine/README.md +58 -0
- package/omega/Agentik_Engine/omega_engine/__init__.py +58 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/audit_arsenal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/barrier.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/bus.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/cli.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/events.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/executor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/mission.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/progress.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/project.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/provider.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/reducer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/report.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/router.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/store.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/supervisor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/task.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/__pycache__/telegram.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/omega_engine/audit.py +96 -0
- package/omega/Agentik_Engine/omega_engine/audit_arsenal.py +314 -0
- package/omega/Agentik_Engine/omega_engine/barrier.py +45 -0
- package/omega/Agentik_Engine/omega_engine/bus.py +45 -0
- package/omega/Agentik_Engine/omega_engine/cli.py +158 -0
- package/omega/Agentik_Engine/omega_engine/events.py +60 -0
- package/omega/Agentik_Engine/omega_engine/executor.py +167 -0
- package/omega/Agentik_Engine/omega_engine/mission.py +145 -0
- package/omega/Agentik_Engine/omega_engine/progress.py +75 -0
- package/omega/Agentik_Engine/omega_engine/project.py +92 -0
- package/omega/Agentik_Engine/omega_engine/provider.py +139 -0
- package/omega/Agentik_Engine/omega_engine/reducer.py +76 -0
- package/omega/Agentik_Engine/omega_engine/report.py +146 -0
- package/omega/Agentik_Engine/omega_engine/router.py +34 -0
- package/omega/Agentik_Engine/omega_engine/store.py +97 -0
- package/omega/Agentik_Engine/omega_engine/supervisor.py +69 -0
- package/omega/Agentik_Engine/omega_engine/task.py +91 -0
- package/omega/Agentik_Engine/omega_engine/telegram.py +115 -0
- package/omega/Agentik_Engine/pyproject.toml +31 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_audit_arsenal.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_executor.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_mission.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_progress.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_project.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_reducer.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/__pycache__/test_report.cpython-313.pyc +0 -0
- package/omega/Agentik_Engine/tests/test_audit_arsenal.py +80 -0
- package/omega/Agentik_Engine/tests/test_executor.py +96 -0
- package/omega/Agentik_Engine/tests/test_mission.py +64 -0
- package/omega/Agentik_Engine/tests/test_progress.py +69 -0
- package/omega/Agentik_Engine/tests/test_project.py +61 -0
- package/omega/Agentik_Engine/tests/test_reducer.py +144 -0
- package/omega/Agentik_Engine/tests/test_report.py +88 -0
- package/omega/Agentik_Extra/README.md +37 -0
- package/omega/Agentik_Extra/etc/agentik.env.example +19 -0
- package/omega/Agentik_Extra/etc/structure.yaml +46 -0
- package/omega/Agentik_Orchestration/README.md +43 -0
- package/omega/Agentik_Orchestration/autonomous/README.md +29 -0
- package/omega/Agentik_Orchestration/autonomous/example-agents.yaml +85 -0
- package/omega/Agentik_Orchestration/educators/README.md +55 -0
- package/omega/Agentik_Orchestration/topologies/aisb-oracle-worker.yaml +42 -0
- package/omega/Agentik_Orchestration/verifier/audit-router.yaml +26 -0
- package/omega/Agentik_Providers/README.md +62 -0
- package/omega/Agentik_Providers/claude/accounts.example.yaml +28 -0
- package/omega/Agentik_Providers/registry.yaml +30 -0
- package/omega/Agentik_Runtime/README.md +30 -0
- package/omega/Agentik_SSOT/README.md +36 -0
- package/omega/Agentik_SSOT/VERSION +1 -0
- package/omega/Agentik_SSOT/audits/a11yaudit.yaml +69 -0
- package/omega/Agentik_SSOT/audits/apiaudit.yaml +71 -0
- package/omega/Agentik_SSOT/audits/automationaudit.yaml +77 -0
- package/omega/Agentik_SSOT/audits/codeaudit.yaml +63 -0
- package/omega/Agentik_SSOT/audits/copyaudit.yaml +68 -0
- package/omega/Agentik_SSOT/audits/dataaudit.yaml +76 -0
- package/omega/Agentik_SSOT/audits/debugaudit.yaml +75 -0
- package/omega/Agentik_SSOT/audits/dxaudit.yaml +78 -0
- package/omega/Agentik_SSOT/audits/featureaudit.yaml +73 -0
- package/omega/Agentik_SSOT/audits/flowaudit.yaml +72 -0
- package/omega/Agentik_SSOT/audits/logicaudit.yaml +75 -0
- package/omega/Agentik_SSOT/audits/motionaudit.yaml +67 -0
- package/omega/Agentik_SSOT/audits/perfaudit.yaml +71 -0
- package/omega/Agentik_SSOT/audits/refontaudit.yaml +77 -0
- package/omega/Agentik_SSOT/audits/retentionaudit.yaml +84 -0
- package/omega/Agentik_SSOT/audits/secaudit.yaml +73 -0
- package/omega/Agentik_SSOT/audits/seoaudit.yaml +75 -0
- package/omega/Agentik_SSOT/audits/uiuxaudit.yaml +61 -0
- package/omega/Agentik_SSOT/mcp/mcp-catalog.yaml +136 -0
- package/omega/Agentik_SSOT/rules/constitution.md +44 -0
- package/omega/Agentik_SSOT/schemas/event.schema.json +45 -0
- package/omega/Agentik_SSOT/schemas/task.schema.json +54 -0
- package/omega/Agentik_Tools/README.md +42 -0
- package/omega/Agentik_Tools/registry.json +15 -0
- package/package.json +43 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""The Quality Arsenal — forensic audits wired into the OmegaOS verification gate.
|
|
2
|
+
|
|
3
|
+
Standalone: python3 tests/test_audit_arsenal.py
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
9
|
+
|
|
10
|
+
from omega_engine.audit_arsenal import ( # noqa: E402
|
|
11
|
+
ArsenalGate,
|
|
12
|
+
AuditRegistry,
|
|
13
|
+
run_forensic_audit,
|
|
14
|
+
)
|
|
15
|
+
from omega_engine.provider import MockProvider # noqa: E402
|
|
16
|
+
from omega_engine.router import ModelRouter # noqa: E402
|
|
17
|
+
|
|
18
|
+
# the audit definitions live in the SSOT block of the repo
|
|
19
|
+
AUDITS_DIR = Path(__file__).resolve().parents[2] / "Agentik_SSOT" / "audits"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_registry_loads_audits():
|
|
23
|
+
reg = AuditRegistry.load(AUDITS_DIR)
|
|
24
|
+
assert len(reg.all()) >= 1, "no audits loaded from the SSOT"
|
|
25
|
+
code = reg.get("codeaudit")
|
|
26
|
+
assert code is not None and code.domain == "code"
|
|
27
|
+
assert len(code.phases) >= 5, "codeaudit has too few phases"
|
|
28
|
+
assert code.gather, "codeaudit declares no deterministic gather tools"
|
|
29
|
+
print(f" {len(reg.all())} audit(s) loaded from Agentik_SSOT/audits/")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_forensic_audit_runs_and_verdicts():
|
|
33
|
+
reg = AuditRegistry.load(AUDITS_DIR)
|
|
34
|
+
code = reg.get("codeaudit")
|
|
35
|
+
ctx = {"role": "worker", "changed": ["pricing.py"], "path": ".",
|
|
36
|
+
"artifacts": {"files": ["pricing.py"], "summary": "fixed pricing"}}
|
|
37
|
+
verdict = run_forensic_audit(code, ctx, MockProvider())
|
|
38
|
+
assert verdict.audit == "codeaudit"
|
|
39
|
+
assert verdict.verified, f"score={verdict.score}"
|
|
40
|
+
assert verdict.score >= 85
|
|
41
|
+
assert verdict.confidence in ("high", "medium", "low")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_arsenal_gate_selects_and_aggregates():
|
|
45
|
+
reg = AuditRegistry.load(AUDITS_DIR)
|
|
46
|
+
gate = ArsenalGate(reg, ModelRouter.single(MockProvider()))
|
|
47
|
+
ctx = {"role": "worker", "changed": ["pricing.py"], "path": ".",
|
|
48
|
+
"artifacts": {"files": ["pricing.py"], "summary": "done"},
|
|
49
|
+
"runtime_cmd": "true"}
|
|
50
|
+
verdict = gate.verify(ctx)
|
|
51
|
+
assert verdict.verified, f"gate rejected: score={verdict.score}"
|
|
52
|
+
assert gate.last_verdicts, "no forensic verdicts recorded for the report"
|
|
53
|
+
print(f" gate ran runtime + {len(gate.last_verdicts)} forensic audit(s)")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_gate_rejects_when_live_flow_fails():
|
|
57
|
+
# validate-live is non-negotiable: a broken flow can never be VERIFIED.
|
|
58
|
+
reg = AuditRegistry.load(AUDITS_DIR)
|
|
59
|
+
gate = ArsenalGate(reg, ModelRouter.single(MockProvider()))
|
|
60
|
+
ctx = {"role": "worker", "changed": ["x.py"], "path": ".",
|
|
61
|
+
"artifacts": {"files": ["x.py"]}, "runtime_cmd": "false"}
|
|
62
|
+
verdict = gate.verify(ctx)
|
|
63
|
+
assert not verdict.verified, "must reject when the live flow fails"
|
|
64
|
+
assert verdict.score == 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run_all() -> bool:
|
|
68
|
+
tests = [v for k, v in sorted(globals().items())
|
|
69
|
+
if k.startswith("test_") and callable(v)]
|
|
70
|
+
passed = 0
|
|
71
|
+
for t in tests:
|
|
72
|
+
t()
|
|
73
|
+
print(f" PASS {t.__name__}")
|
|
74
|
+
passed += 1
|
|
75
|
+
print(f"\n{passed}/{len(tests)} arsenal tests passed")
|
|
76
|
+
return passed == len(tests)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""End-to-end: a real mission runs through the executor and reaches VERIFIED.
|
|
2
|
+
|
|
3
|
+
THE proof that OmegaOS orchestration works — verified completion, the join
|
|
4
|
+
barrier, and the audit gate (which really runs a subprocess) all exercised with
|
|
5
|
+
a deterministic MockProvider. Runs standalone: python3 tests/test_executor.py
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
13
|
+
|
|
14
|
+
from omega_engine.audit import AuditGate # noqa: E402
|
|
15
|
+
from omega_engine.bus import EventBus # noqa: E402
|
|
16
|
+
from omega_engine.executor import Executor # noqa: E402
|
|
17
|
+
from omega_engine.provider import MockProvider # noqa: E402
|
|
18
|
+
from omega_engine.reducer import reduce_task # noqa: E402
|
|
19
|
+
from omega_engine.router import ModelRouter # noqa: E402
|
|
20
|
+
from omega_engine.store import SQLiteStore # noqa: E402
|
|
21
|
+
from omega_engine.task import Kind, TaskState # noqa: E402
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _engine(plan_size: int = 2):
|
|
25
|
+
db = tempfile.mktemp(suffix=".db")
|
|
26
|
+
store = SQLiteStore(db)
|
|
27
|
+
bus = EventBus(store)
|
|
28
|
+
router = ModelRouter.single(MockProvider(plan_size=plan_size))
|
|
29
|
+
executor = Executor(store, bus, router, AuditGate())
|
|
30
|
+
return store, executor, db
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_mission_runs_to_verified():
|
|
34
|
+
store, executor, db = _engine(plan_size=3)
|
|
35
|
+
result = executor.run_mission("fix the pricing bug")
|
|
36
|
+
assert result.verified, f"mission did not complete: {result.final_state}"
|
|
37
|
+
assert result.final_state is TaskState.COMPLETED
|
|
38
|
+
workers = [t for t in result.tasks.values() if t.kind is Kind.EXECUTOR]
|
|
39
|
+
assert len(workers) == 3, f"expected 3 workers, got {len(workers)}"
|
|
40
|
+
for w in workers:
|
|
41
|
+
assert reduce_task(store.events_for(w.id)) is TaskState.COMPLETED
|
|
42
|
+
store.close()
|
|
43
|
+
os.remove(db)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_every_emitted_sequence_is_fsm_legal():
|
|
47
|
+
# reduce_task raises IllegalTransition on a bad sequence. If this passes,
|
|
48
|
+
# every event the executor emitted is a legal FSM transition.
|
|
49
|
+
store, executor, db = _engine()
|
|
50
|
+
executor.run_mission("ship the feature")
|
|
51
|
+
for tid in store.task_ids():
|
|
52
|
+
reduce_task(store.events_for(tid)) # raises if illegal
|
|
53
|
+
store.close()
|
|
54
|
+
os.remove(db)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_worker_passes_through_the_audit_gate():
|
|
58
|
+
# completion is verified: a worker must go VERIFYING before VERIFIED.
|
|
59
|
+
store, executor, db = _engine(plan_size=1)
|
|
60
|
+
result = executor.run_mission("one task")
|
|
61
|
+
worker = next(t for t in result.tasks.values() if t.kind is Kind.EXECUTOR)
|
|
62
|
+
types = [e.type.value for e in store.events_for(worker.id)]
|
|
63
|
+
assert "task.verifying" in types
|
|
64
|
+
assert "task.verified" in types
|
|
65
|
+
assert types.index("task.verifying") < types.index("task.verified")
|
|
66
|
+
assert types[-1] == "task.completed"
|
|
67
|
+
store.close()
|
|
68
|
+
os.remove(db)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_root_completes_only_after_children():
|
|
72
|
+
# the join barrier: scope.joinable is emitted on the root, and only after
|
|
73
|
+
# every worker is already terminal.
|
|
74
|
+
store, executor, db = _engine(plan_size=2)
|
|
75
|
+
result = executor.run_mission("barrier check")
|
|
76
|
+
root_events = [e.type.value for e in store.events_for(result.root.id)]
|
|
77
|
+
assert "scope.joinable" in root_events
|
|
78
|
+
assert root_events.index("scope.joinable") < root_events.index("task.completed")
|
|
79
|
+
store.close()
|
|
80
|
+
os.remove(db)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _run_all() -> bool:
|
|
84
|
+
tests = [v for k, v in sorted(globals().items())
|
|
85
|
+
if k.startswith("test_") and callable(v)]
|
|
86
|
+
passed = 0
|
|
87
|
+
for t in tests:
|
|
88
|
+
t()
|
|
89
|
+
print(f" PASS {t.__name__}")
|
|
90
|
+
passed += 1
|
|
91
|
+
print(f"\n{passed}/{len(tests)} executor tests passed")
|
|
92
|
+
return passed == len(tests)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Mission coordinator — run_mission end to end, always with a PDF report.
|
|
2
|
+
|
|
3
|
+
Standalone: python3 tests/test_mission.py
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
12
|
+
|
|
13
|
+
# force the deterministic provider even if ANTHROPIC_API_KEY is in the env
|
|
14
|
+
os.environ["OMEGA_PROVIDER"] = "mock"
|
|
15
|
+
|
|
16
|
+
from omega_engine.mission import run_mission # noqa: E402
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _pdfgen_bin() -> str | None:
|
|
20
|
+
env = os.environ.get("OMEGA_PDFGEN")
|
|
21
|
+
if env and Path(env).exists():
|
|
22
|
+
return env
|
|
23
|
+
fallback = Path.home() / ".claude" / "lib" / "pdfgen.sh"
|
|
24
|
+
return str(fallback) if fallback.exists() else None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_run_mission_end_to_end():
|
|
28
|
+
binary = _pdfgen_bin()
|
|
29
|
+
if binary:
|
|
30
|
+
os.environ["OMEGA_PDFGEN"] = binary
|
|
31
|
+
home = Path(tempfile.mkdtemp())
|
|
32
|
+
try:
|
|
33
|
+
outcome = run_mission("add a logout button", omega_home=home)
|
|
34
|
+
assert outcome.result.verified, (
|
|
35
|
+
f"mission failed: {outcome.result.final_state}")
|
|
36
|
+
assert outcome.progress_pct == 100, f"pct={outcome.progress_pct}"
|
|
37
|
+
if binary:
|
|
38
|
+
assert outcome.report_pdf is not None, "no report produced"
|
|
39
|
+
assert outcome.report_pdf.exists(), "report PDF missing on disk"
|
|
40
|
+
assert outcome.report_pdf.stat().st_size > 5000
|
|
41
|
+
print(f" mission verified + report: "
|
|
42
|
+
f"{outcome.report_pdf.stat().st_size // 1024} KB PDF")
|
|
43
|
+
else:
|
|
44
|
+
print(" mission verified (pdfgen not on host — report skipped)")
|
|
45
|
+
# the event log persisted under Agentik_Runtime
|
|
46
|
+
assert (home / "Agentik_Runtime" / "eventlog" / "omega.db").exists()
|
|
47
|
+
finally:
|
|
48
|
+
shutil.rmtree(home, ignore_errors=True)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _run_all() -> bool:
|
|
52
|
+
tests = [v for k, v in sorted(globals().items())
|
|
53
|
+
if k.startswith("test_") and callable(v)]
|
|
54
|
+
passed = 0
|
|
55
|
+
for t in tests:
|
|
56
|
+
t()
|
|
57
|
+
print(f" PASS {t.__name__}")
|
|
58
|
+
passed += 1
|
|
59
|
+
print(f"\n{passed}/{len(tests)} mission tests passed")
|
|
60
|
+
return passed == len(tests)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Progress tracking — the bus subscriber turns events into a progress bar.
|
|
2
|
+
|
|
3
|
+
Standalone: python3 tests/test_progress.py
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
11
|
+
|
|
12
|
+
from omega_engine.audit import AuditGate # noqa: E402
|
|
13
|
+
from omega_engine.bus import EventBus # noqa: E402
|
|
14
|
+
from omega_engine.executor import Executor # noqa: E402
|
|
15
|
+
from omega_engine.progress import ProgressTracker # noqa: E402
|
|
16
|
+
from omega_engine.provider import MockProvider # noqa: E402
|
|
17
|
+
from omega_engine.router import ModelRouter # noqa: E402
|
|
18
|
+
from omega_engine.store import SQLiteStore # noqa: E402
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _run(plan_size=3):
|
|
22
|
+
db = tempfile.mktemp(suffix=".db")
|
|
23
|
+
store = SQLiteStore(db)
|
|
24
|
+
bus = EventBus(store)
|
|
25
|
+
tracker = ProgressTracker()
|
|
26
|
+
bus.subscribe(tracker.on_event) # <-- progress is a bus subscriber
|
|
27
|
+
executor = Executor(store, bus, ModelRouter.single(MockProvider(plan_size)),
|
|
28
|
+
AuditGate())
|
|
29
|
+
result = executor.run_mission("track me")
|
|
30
|
+
store.close()
|
|
31
|
+
os.remove(db)
|
|
32
|
+
return tracker, result
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_progress_reaches_100():
|
|
36
|
+
tracker, result = _run(plan_size=3)
|
|
37
|
+
prog = tracker.progress(result.mission_id)
|
|
38
|
+
assert prog is not None, "no progress recorded"
|
|
39
|
+
# 1 oracle + 3 workers = 4 tasks, all verified
|
|
40
|
+
assert prog.total_tasks == 4, f"total={prog.total_tasks}"
|
|
41
|
+
assert prog.verified == 4, f"verified={prog.verified}"
|
|
42
|
+
assert prog.failed == 0
|
|
43
|
+
assert prog.pct == 100, f"pct={prog.pct}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_progress_bar_renders():
|
|
47
|
+
tracker, result = _run(plan_size=2)
|
|
48
|
+
prog = tracker.progress(result.mission_id)
|
|
49
|
+
bar = prog.bar(width=20)
|
|
50
|
+
assert len(bar) == 20
|
|
51
|
+
block = prog.render(title="Mission")
|
|
52
|
+
assert "100%" in block
|
|
53
|
+
assert "Mission" in block
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _run_all() -> bool:
|
|
57
|
+
tests = [v for k, v in sorted(globals().items())
|
|
58
|
+
if k.startswith("test_") and callable(v)]
|
|
59
|
+
passed = 0
|
|
60
|
+
for t in tests:
|
|
61
|
+
t()
|
|
62
|
+
print(f" PASS {t.__name__}")
|
|
63
|
+
passed += 1
|
|
64
|
+
print(f"\n{passed}/{len(tests)} progress tests passed")
|
|
65
|
+
return passed == len(tests)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Project creation — structure + registry. Standalone: python3 tests/test_project.py"""
|
|
2
|
+
import shutil
|
|
3
|
+
import sys
|
|
4
|
+
import tempfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
8
|
+
|
|
9
|
+
from omega_engine.project import create_project, load_projects, slugify # noqa: E402
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_slugify():
|
|
13
|
+
assert slugify("My Cool Project!") == "my-cool-project"
|
|
14
|
+
assert slugify("Causio") == "causio"
|
|
15
|
+
assert slugify(" ") == "project"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_create_project_builds_structure():
|
|
19
|
+
home = Path(tempfile.mkdtemp())
|
|
20
|
+
try:
|
|
21
|
+
p = create_project("Pricing Tool", omega_home=home) # no Telegram
|
|
22
|
+
assert p.slug == "pricing-tool"
|
|
23
|
+
assert Path(p.path).is_dir()
|
|
24
|
+
assert (Path(p.path) / "README.md").exists()
|
|
25
|
+
assert p.topic_id is None # no Telegram bridge given
|
|
26
|
+
# registered
|
|
27
|
+
projects = load_projects(home)
|
|
28
|
+
assert len(projects) == 1
|
|
29
|
+
assert projects[0].slug == "pricing-tool"
|
|
30
|
+
finally:
|
|
31
|
+
shutil.rmtree(home, ignore_errors=True)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_duplicate_project_rejected():
|
|
35
|
+
home = Path(tempfile.mkdtemp())
|
|
36
|
+
try:
|
|
37
|
+
create_project("Dup", omega_home=home)
|
|
38
|
+
raised = False
|
|
39
|
+
try:
|
|
40
|
+
create_project("Dup", omega_home=home)
|
|
41
|
+
except ValueError:
|
|
42
|
+
raised = True
|
|
43
|
+
assert raised, "duplicate project must be rejected"
|
|
44
|
+
finally:
|
|
45
|
+
shutil.rmtree(home, ignore_errors=True)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _run_all() -> bool:
|
|
49
|
+
tests = [v for k, v in sorted(globals().items())
|
|
50
|
+
if k.startswith("test_") and callable(v)]
|
|
51
|
+
passed = 0
|
|
52
|
+
for t in tests:
|
|
53
|
+
t()
|
|
54
|
+
print(f" PASS {t.__name__}")
|
|
55
|
+
passed += 1
|
|
56
|
+
print(f"\n{passed}/{len(tests)} project tests passed")
|
|
57
|
+
return passed == len(tests)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Tests for the reducer and the join barrier — the engine's anti-hallucination core.
|
|
2
|
+
|
|
3
|
+
Runs under pytest, or standalone: python3 tests/test_reducer.py
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# allow `python3 tests/test_reducer.py` from anywhere
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
10
|
+
|
|
11
|
+
from omega_engine.barrier import ScopeStatus, scope_status # noqa: E402
|
|
12
|
+
from omega_engine.events import Event, EventType # noqa: E402
|
|
13
|
+
from omega_engine.reducer import IllegalTransition, reduce_task # noqa: E402
|
|
14
|
+
from omega_engine.task import TaskState # noqa: E402
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _ev(task_id: str, etype: EventType) -> Event:
|
|
18
|
+
return Event(task_id=task_id, type=etype)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_happy_path_to_completed():
|
|
22
|
+
events = [
|
|
23
|
+
_ev("t1", EventType.CREATED),
|
|
24
|
+
_ev("t1", EventType.DISPATCHED),
|
|
25
|
+
_ev("t1", EventType.STARTED),
|
|
26
|
+
_ev("t1", EventType.CLAIMED_DONE),
|
|
27
|
+
_ev("t1", EventType.VERIFYING),
|
|
28
|
+
_ev("t1", EventType.VERIFIED),
|
|
29
|
+
_ev("t1", EventType.COMPLETED),
|
|
30
|
+
]
|
|
31
|
+
assert reduce_task(events) is TaskState.COMPLETED
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_claimed_done_is_not_completion():
|
|
35
|
+
# THE crux test: a worker claiming done does NOT reach verified/terminal.
|
|
36
|
+
events = [
|
|
37
|
+
_ev("t1", EventType.CREATED),
|
|
38
|
+
_ev("t1", EventType.DISPATCHED),
|
|
39
|
+
_ev("t1", EventType.STARTED),
|
|
40
|
+
_ev("t1", EventType.CLAIMED_DONE),
|
|
41
|
+
]
|
|
42
|
+
state = reduce_task(events)
|
|
43
|
+
assert state is TaskState.CLAIMED_DONE
|
|
44
|
+
assert state is not TaskState.VERIFIED
|
|
45
|
+
assert state is not TaskState.COMPLETED
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_reject_then_retry_then_pass():
|
|
49
|
+
events = [
|
|
50
|
+
_ev("t1", EventType.CREATED),
|
|
51
|
+
_ev("t1", EventType.DISPATCHED),
|
|
52
|
+
_ev("t1", EventType.STARTED),
|
|
53
|
+
_ev("t1", EventType.CLAIMED_DONE),
|
|
54
|
+
_ev("t1", EventType.VERIFYING),
|
|
55
|
+
_ev("t1", EventType.REJECTED),
|
|
56
|
+
_ev("t1", EventType.DISPATCHED), # retry within budget
|
|
57
|
+
_ev("t1", EventType.STARTED),
|
|
58
|
+
_ev("t1", EventType.CLAIMED_DONE),
|
|
59
|
+
_ev("t1", EventType.VERIFYING),
|
|
60
|
+
_ev("t1", EventType.VERIFIED),
|
|
61
|
+
_ev("t1", EventType.COMPLETED),
|
|
62
|
+
]
|
|
63
|
+
assert reduce_task(events) is TaskState.COMPLETED
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_deadman_kills_a_live_task():
|
|
67
|
+
events = [
|
|
68
|
+
_ev("t1", EventType.CREATED),
|
|
69
|
+
_ev("t1", EventType.DISPATCHED),
|
|
70
|
+
_ev("t1", EventType.STARTED),
|
|
71
|
+
_ev("t1", EventType.FAILED), # deadman fires from RUNNING
|
|
72
|
+
]
|
|
73
|
+
assert reduce_task(events) is TaskState.FAILED
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_illegal_transition_raises():
|
|
77
|
+
# cannot START a task that was never DISPATCHED
|
|
78
|
+
events = [_ev("t1", EventType.CREATED), _ev("t1", EventType.STARTED)]
|
|
79
|
+
try:
|
|
80
|
+
reduce_task(events)
|
|
81
|
+
raised = False
|
|
82
|
+
except IllegalTransition:
|
|
83
|
+
raised = True
|
|
84
|
+
assert raised, "an illegal transition must raise, not corrupt"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_failed_on_terminal_is_illegal():
|
|
88
|
+
events = [
|
|
89
|
+
_ev("t1", EventType.CREATED),
|
|
90
|
+
_ev("t1", EventType.DISPATCHED),
|
|
91
|
+
_ev("t1", EventType.STARTED),
|
|
92
|
+
_ev("t1", EventType.CLAIMED_DONE),
|
|
93
|
+
_ev("t1", EventType.VERIFYING),
|
|
94
|
+
_ev("t1", EventType.VERIFIED),
|
|
95
|
+
_ev("t1", EventType.COMPLETED),
|
|
96
|
+
_ev("t1", EventType.FAILED), # cannot fail a COMPLETED task
|
|
97
|
+
]
|
|
98
|
+
try:
|
|
99
|
+
reduce_task(events)
|
|
100
|
+
raised = False
|
|
101
|
+
except IllegalTransition:
|
|
102
|
+
raised = True
|
|
103
|
+
assert raised
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_empty_stream_raises():
|
|
107
|
+
try:
|
|
108
|
+
reduce_task([])
|
|
109
|
+
raised = False
|
|
110
|
+
except IllegalTransition:
|
|
111
|
+
raised = True
|
|
112
|
+
assert raised
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_barrier_running_until_all_terminal():
|
|
116
|
+
assert scope_status([TaskState.RUNNING, TaskState.COMPLETED]) is ScopeStatus.RUNNING
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_barrier_joinable_when_all_completed():
|
|
120
|
+
assert scope_status([TaskState.COMPLETED, TaskState.COMPLETED]) is ScopeStatus.JOINABLE
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_barrier_partial_when_a_child_failed():
|
|
124
|
+
assert scope_status([TaskState.COMPLETED, TaskState.FAILED]) is ScopeStatus.PARTIAL
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_barrier_empty_scope_is_joinable():
|
|
128
|
+
assert scope_status([]) is ScopeStatus.JOINABLE
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _run_all() -> bool:
|
|
132
|
+
tests = [v for k, v in sorted(globals().items())
|
|
133
|
+
if k.startswith("test_") and callable(v)]
|
|
134
|
+
passed = 0
|
|
135
|
+
for t in tests:
|
|
136
|
+
t()
|
|
137
|
+
print(f" PASS {t.__name__}")
|
|
138
|
+
passed += 1
|
|
139
|
+
print(f"\n{passed}/{len(tests)} tests passed")
|
|
140
|
+
return passed == len(tests)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Mission report -> real PDF, end to end.
|
|
2
|
+
|
|
3
|
+
Runs a mission, builds the whitepaper report, renders it through the pdfgen
|
|
4
|
+
tool, and asserts a real PDF lands on disk. Standalone: python3 tests/test_report.py
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
12
|
+
|
|
13
|
+
from omega_engine.audit import AuditGate # noqa: E402
|
|
14
|
+
from omega_engine.bus import EventBus # noqa: E402
|
|
15
|
+
from omega_engine.executor import Executor # noqa: E402
|
|
16
|
+
from omega_engine.provider import MockProvider # noqa: E402
|
|
17
|
+
from omega_engine.report import build_report, generate_mission_report # noqa: E402
|
|
18
|
+
from omega_engine.router import ModelRouter # noqa: E402
|
|
19
|
+
from omega_engine.store import SQLiteStore # noqa: E402
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _pdfgen_bin() -> str | None:
|
|
23
|
+
env = os.environ.get("OMEGA_PDFGEN")
|
|
24
|
+
if env and Path(env).exists():
|
|
25
|
+
return env
|
|
26
|
+
fallback = Path.home() / ".claude" / "lib" / "pdfgen.sh"
|
|
27
|
+
return str(fallback) if fallback.exists() else None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _run_mission():
|
|
31
|
+
db = tempfile.mktemp(suffix=".db")
|
|
32
|
+
store = SQLiteStore(db)
|
|
33
|
+
bus = EventBus(store)
|
|
34
|
+
executor = Executor(store, bus, ModelRouter.single(MockProvider(plan_size=3)),
|
|
35
|
+
AuditGate())
|
|
36
|
+
result = executor.run_mission("fix the checkout flow")
|
|
37
|
+
return store, result, db
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_build_report_shape():
|
|
41
|
+
"""The report data matches the pdfgen whitepaper schema — no PDF needed."""
|
|
42
|
+
store, result, db = _run_mission()
|
|
43
|
+
data = build_report(result, store)
|
|
44
|
+
assert data["template"] == "whitepaper"
|
|
45
|
+
assert data["theme"] == "agentik"
|
|
46
|
+
assert data["abstract"] and data["title"]
|
|
47
|
+
assert len(data["sections"]) == 4
|
|
48
|
+
for s in data["sections"]:
|
|
49
|
+
assert s["index"] and s["title"] and s["body"]
|
|
50
|
+
store.close()
|
|
51
|
+
os.remove(db)
|
|
52
|
+
print(" report schema OK (4 sections, whitepaper/agentik)")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_render_real_pdf():
|
|
56
|
+
"""Render the report through the real pdfgen tool — a genuine PDF on disk."""
|
|
57
|
+
binary = _pdfgen_bin()
|
|
58
|
+
if not binary:
|
|
59
|
+
print(" SKIP — pdfgen tool not found on this host")
|
|
60
|
+
return
|
|
61
|
+
store, result, db = _run_mission()
|
|
62
|
+
out_dir = tempfile.mkdtemp()
|
|
63
|
+
pdf = generate_mission_report(result, store, out_dir, pdfgen_bin=binary)
|
|
64
|
+
assert pdf.exists(), "no PDF produced"
|
|
65
|
+
size = pdf.stat().st_size
|
|
66
|
+
assert size > 5000, f"PDF suspiciously small: {size} bytes"
|
|
67
|
+
head = pdf.read_bytes()[:5]
|
|
68
|
+
assert head.startswith(b"%PDF"), "output is not a PDF"
|
|
69
|
+
print(f" real PDF rendered: {pdf} ({size // 1024} KB)")
|
|
70
|
+
pdf.unlink()
|
|
71
|
+
store.close()
|
|
72
|
+
os.remove(db)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _run_all() -> bool:
|
|
76
|
+
tests = [v for k, v in sorted(globals().items())
|
|
77
|
+
if k.startswith("test_") and callable(v)]
|
|
78
|
+
passed = 0
|
|
79
|
+
for t in tests:
|
|
80
|
+
t()
|
|
81
|
+
print(f" PASS {t.__name__}")
|
|
82
|
+
passed += 1
|
|
83
|
+
print(f"\n{passed}/{len(tests)} report tests passed")
|
|
84
|
+
return passed == len(tests)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
sys.exit(0 if _run_all() else 1)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Agentik_Extra — ephemeral + config + secrets
|
|
2
|
+
|
|
3
|
+
> **Nature:** periphery · **Lifecycle:** disposable / sensitive · **Git:** not versioned
|
|
4
|
+
|
|
5
|
+
The eighth block absorbs everything that would otherwise clutter the master
|
|
6
|
+
folder, so `~/Omega/` stays at exactly eight clean entries.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Agentik_Extra/
|
|
10
|
+
├── var/
|
|
11
|
+
│ ├── cache/ XDG_CACHE_HOME points here
|
|
12
|
+
│ ├── tmp/ TMPDIR points here
|
|
13
|
+
│ ├── logs/ rotated automatically
|
|
14
|
+
│ └── .install-state the installer's resume marker
|
|
15
|
+
├── staging/
|
|
16
|
+
│ └── promotion/ the educators' pipeline: staged → audited → promoted to SSOT
|
|
17
|
+
└── etc/
|
|
18
|
+
├── agentik.env the environment that forces the tidy layout
|
|
19
|
+
├── structure.yaml the arborescence manifest — checked hourly for drift
|
|
20
|
+
└── secrets/ the encrypted vault — API keys, tokens (chmod 700)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Three regimes, one block
|
|
24
|
+
|
|
25
|
+
- **`var/`** — pure ephemeral. Safe to wipe at any time. All cache and temp files
|
|
26
|
+
point here, so nothing scatters across the home directory or `/tmp`.
|
|
27
|
+
- **`staging/`** — the educators' workspace. An educator writes a proposed change
|
|
28
|
+
to `promotion/`; the audit gate validates it; only then is it promoted into
|
|
29
|
+
`Agentik_SSOT/`. Never silent self-modification.
|
|
30
|
+
- **`etc/`** — configuration and secrets.
|
|
31
|
+
|
|
32
|
+
## Secrets — a vault, not a folder
|
|
33
|
+
|
|
34
|
+
`etc/secrets/` is `chmod 700` and holds an **encrypted** vault (age/sops or the
|
|
35
|
+
OS keyring). The installer never writes a plaintext secret into the tree;
|
|
36
|
+
config files store a *reference* (`secret_ref: COMPOSIO_API_KEY`), resolved at
|
|
37
|
+
use time. `secrets/` is git-ignored without exception.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Omega OS — environment.
|
|
2
|
+
# The installer copies this to agentik.env and sources it from the shell rc.
|
|
3
|
+
# These variables FORCE every tool to range its files inside the rack.
|
|
4
|
+
|
|
5
|
+
# --- the master folder ---
|
|
6
|
+
export OMEGA_HOME="$HOME/Omega"
|
|
7
|
+
|
|
8
|
+
# --- the only directory added to PATH (symlinks to installed tools) ---
|
|
9
|
+
export PATH="$OMEGA_HOME/Agentik_Tools/bin:$PATH"
|
|
10
|
+
|
|
11
|
+
# --- redirect all cache + temp into the Extra block ---
|
|
12
|
+
export XDG_CACHE_HOME="$OMEGA_HOME/Agentik_Extra/var/cache"
|
|
13
|
+
export TMPDIR="$OMEGA_HOME/Agentik_Extra/var/tmp"
|
|
14
|
+
|
|
15
|
+
# --- the runtime event log (the source of truth) ---
|
|
16
|
+
export OMEGA_EVENTLOG="$OMEGA_HOME/Agentik_Runtime/eventlog/omega.db"
|
|
17
|
+
|
|
18
|
+
# --- secrets are loaded from the encrypted vault, never set here ---
|
|
19
|
+
# export COMPOSIO_API_KEY=... # NO — use Agentik_Extra/etc/secrets/
|