cortex-loop 0.1.0a1__py3-none-any.whl

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.
Files changed (52) hide show
  1. cortex/__init__.py +7 -0
  2. cortex/adapters.py +339 -0
  3. cortex/blocklist.py +51 -0
  4. cortex/challenges.py +210 -0
  5. cortex/cli.py +7 -0
  6. cortex/core.py +601 -0
  7. cortex/core_helpers.py +190 -0
  8. cortex/data/identity_preamble.md +5 -0
  9. cortex/data/layer1_part_a.md +65 -0
  10. cortex/data/layer1_part_b.md +17 -0
  11. cortex/executive.py +295 -0
  12. cortex/foundation.py +185 -0
  13. cortex/genome.py +348 -0
  14. cortex/graveyard.py +226 -0
  15. cortex/hooks/__init__.py +27 -0
  16. cortex/hooks/_shared.py +167 -0
  17. cortex/hooks/post_tool_use.py +13 -0
  18. cortex/hooks/pre_tool_use.py +13 -0
  19. cortex/hooks/session_start.py +13 -0
  20. cortex/hooks/stop.py +13 -0
  21. cortex/invariants.py +258 -0
  22. cortex/packs.py +118 -0
  23. cortex/repomap.py +6 -0
  24. cortex/requirements.py +497 -0
  25. cortex/retry.py +312 -0
  26. cortex/stop_contract.py +217 -0
  27. cortex/stop_payload.py +122 -0
  28. cortex/stop_policy.py +100 -0
  29. cortex/stop_runtime.py +400 -0
  30. cortex/stop_signals.py +75 -0
  31. cortex/store.py +793 -0
  32. cortex/templates/__init__.py +10 -0
  33. cortex/utils.py +58 -0
  34. cortex_loop-0.1.0a1.dist-info/METADATA +121 -0
  35. cortex_loop-0.1.0a1.dist-info/RECORD +52 -0
  36. cortex_loop-0.1.0a1.dist-info/WHEEL +5 -0
  37. cortex_loop-0.1.0a1.dist-info/entry_points.txt +3 -0
  38. cortex_loop-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  39. cortex_loop-0.1.0a1.dist-info/top_level.txt +3 -0
  40. cortex_ops_cli/__init__.py +3 -0
  41. cortex_ops_cli/_adapter_validation.py +119 -0
  42. cortex_ops_cli/_check_report.py +454 -0
  43. cortex_ops_cli/_check_report_output.py +270 -0
  44. cortex_ops_cli/_openai_bridge_probe.py +241 -0
  45. cortex_ops_cli/_openai_bridge_protocol.py +469 -0
  46. cortex_ops_cli/_runtime_profile_templates.py +341 -0
  47. cortex_ops_cli/_runtime_profiles.py +445 -0
  48. cortex_ops_cli/gemini_hooks.py +301 -0
  49. cortex_ops_cli/main.py +911 -0
  50. cortex_ops_cli/openai_app_server_bridge.py +375 -0
  51. cortex_repomap/__init__.py +1 -0
  52. cortex_repomap/engine.py +1201 -0
cortex/invariants.py ADDED
@@ -0,0 +1,258 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib.util
4
+ import os
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from pathlib import Path
11
+ from typing import Any, Iterable
12
+
13
+ from .genome import HooksConfig, InvariantsConfig
14
+ from .store import SQLiteStore
15
+
16
+
17
+ @dataclass(slots=True)
18
+ class InvariantCaseResult:
19
+ test_path: str
20
+ status: str
21
+ duration_ms: int
22
+ stdout: str
23
+ stderr: str
24
+
25
+ def to_dict(self) -> dict[str, Any]:
26
+ return {
27
+ "test_path": self.test_path,
28
+ "status": self.status,
29
+ "duration_ms": self.duration_ms,
30
+ "stdout": self.stdout,
31
+ "stderr": self.stderr,
32
+ }
33
+
34
+
35
+ @dataclass(slots=True)
36
+ class InvariantReport:
37
+ configured_paths: list[str]
38
+ results: list[InvariantCaseResult] = field(default_factory=list)
39
+ diagnostics: list[dict[str, Any]] = field(default_factory=list)
40
+ ok: bool = True
41
+ had_errors: bool = False
42
+ recommend_revert: bool = False
43
+
44
+ def to_dict(self) -> dict[str, Any]:
45
+ return {
46
+ "configured_paths": self.configured_paths,
47
+ "results": [r.to_dict() for r in self.results],
48
+ "diagnostics": self.diagnostics,
49
+ "ok": self.ok,
50
+ "had_errors": self.had_errors,
51
+ "recommend_revert": self.recommend_revert,
52
+ }
53
+
54
+
55
+ class InvariantRunner:
56
+ def __init__(
57
+ self,
58
+ repo_root: Path,
59
+ store: SQLiteStore,
60
+ config: InvariantsConfig,
61
+ hooks_config: HooksConfig,
62
+ *,
63
+ trust_profile: str = "trusted",
64
+ ) -> None:
65
+ self.repo_root = repo_root.resolve()
66
+ self.store = store
67
+ self.config = config
68
+ self.hooks_config = hooks_config
69
+ self.trust_profile = trust_profile if trust_profile in {"trusted", "untrusted"} else "untrusted"
70
+
71
+ def run(self, session_id: str, extra_pytest_args: Iterable[str] | None = None) -> InvariantReport:
72
+ report = InvariantReport(configured_paths=list(self.config.suite_paths))
73
+ args = list(extra_pytest_args or [])
74
+ if self.trust_profile == "untrusted" and self.config.execution_mode == "host":
75
+ result = InvariantCaseResult(
76
+ test_path="__policy__",
77
+ status="error",
78
+ duration_ms=0,
79
+ stdout="",
80
+ stderr=(
81
+ "Host invariant execution is blocked for trust_profile='untrusted'. "
82
+ "Set [invariants].execution_mode='container' or [project].trust_profile='trusted'."
83
+ ),
84
+ )
85
+ report.results.append(result)
86
+ report.diagnostics.append(_invariant_diagnostic(result))
87
+ report.ok = False
88
+ report.had_errors = True
89
+ self.store.record_invariant_result(
90
+ session_id=session_id,
91
+ test_path=result.test_path,
92
+ status=result.status,
93
+ duration_ms=result.duration_ms,
94
+ stdout=result.stdout,
95
+ stderr=result.stderr,
96
+ )
97
+ report.recommend_revert = self.hooks_config.recommend_revert_on_invariant_failure
98
+ return report
99
+
100
+ for suite_path in self.config.suite_paths:
101
+ result = self._run_one(session_id=session_id, suite_path=suite_path, extra_args=args)
102
+ report.results.append(result)
103
+ if result.status in {"fail", "error", "missing"}:
104
+ report.diagnostics.append(_invariant_diagnostic(result))
105
+ self.store.record_invariant_result(
106
+ session_id=session_id,
107
+ test_path=result.test_path,
108
+ status=result.status,
109
+ duration_ms=result.duration_ms,
110
+ stdout=result.stdout,
111
+ stderr=result.stderr,
112
+ )
113
+ if result.status in {"fail", "error", "missing"}:
114
+ report.ok = False
115
+ if result.status == "error":
116
+ report.had_errors = True
117
+
118
+ if not report.ok:
119
+ report.recommend_revert = self.hooks_config.recommend_revert_on_invariant_failure
120
+ return report
121
+
122
+ def promote_session_test(self, session_id: str, source_path: str | Path) -> Path:
123
+ source = (self.repo_root / source_path).resolve() if not Path(source_path).is_absolute() else Path(source_path)
124
+ target_dir = self.repo_root / self.config.graduation.target_dir
125
+ target_dir.mkdir(parents=True, exist_ok=True)
126
+ target = target_dir / source.name
127
+ shutil.copy2(source, target)
128
+ self.store.record_invariant_result(
129
+ session_id=session_id,
130
+ test_path=str(target.relative_to(self.repo_root)),
131
+ status="graduated",
132
+ duration_ms=0,
133
+ stdout="",
134
+ stderr="",
135
+ graduated_from=str(source),
136
+ )
137
+ return target
138
+
139
+ def _run_one(self, *, session_id: str, suite_path: str, extra_args: list[str]) -> InvariantCaseResult:
140
+ path = self.repo_root / suite_path
141
+ if not path.exists():
142
+ return InvariantCaseResult(
143
+ test_path=suite_path,
144
+ status="missing",
145
+ duration_ms=0,
146
+ stdout="",
147
+ stderr=f"Invariant path not found: {suite_path}",
148
+ )
149
+
150
+ started = time.perf_counter()
151
+ try:
152
+ cmd = self._pytest_command(path=path, suite_path=suite_path, extra_args=extra_args, session_id=session_id)
153
+ except ValueError as exc:
154
+ return InvariantCaseResult(
155
+ test_path=suite_path,
156
+ status="error",
157
+ duration_ms=int((time.perf_counter() - started) * 1000),
158
+ stdout="",
159
+ stderr=str(exc),
160
+ )
161
+ env = None
162
+ if self.config.execution_mode != "container":
163
+ env = os.environ.copy()
164
+ env["CORTEX_SESSION_ID"] = str(session_id)
165
+ env["CORTEX_PROJECT_ROOT"] = str(self.repo_root)
166
+ try:
167
+ proc = subprocess.run(
168
+ cmd,
169
+ cwd=self.repo_root,
170
+ env=env,
171
+ capture_output=True,
172
+ text=True,
173
+ check=False,
174
+ )
175
+ except FileNotFoundError as exc:
176
+ return InvariantCaseResult(
177
+ test_path=suite_path,
178
+ status="error",
179
+ duration_ms=int((time.perf_counter() - started) * 1000),
180
+ stdout="",
181
+ stderr=str(exc),
182
+ )
183
+
184
+ duration_ms = int((time.perf_counter() - started) * 1000)
185
+ status = "pass" if proc.returncode == 0 else "fail"
186
+ return InvariantCaseResult(
187
+ test_path=suite_path,
188
+ status=status,
189
+ duration_ms=duration_ms,
190
+ stdout=proc.stdout.strip(),
191
+ stderr=proc.stderr.strip(),
192
+ )
193
+
194
+ def _pytest_command(
195
+ self,
196
+ *,
197
+ path: Path,
198
+ suite_path: str,
199
+ extra_args: list[str],
200
+ session_id: str,
201
+ ) -> list[str]:
202
+ if self.config.execution_mode != "container":
203
+ return self._host_pytest_command(path=path, extra_args=extra_args)
204
+ target = self._container_suite_path(path, suite_path)
205
+ return [
206
+ self.config.container_engine,
207
+ "run",
208
+ "--rm",
209
+ "-e",
210
+ f"CORTEX_SESSION_ID={session_id}",
211
+ "-e",
212
+ f"CORTEX_PROJECT_ROOT={self.config.container_workdir}",
213
+ "-v",
214
+ f"{self.repo_root}:{self.config.container_workdir}",
215
+ "-w",
216
+ self.config.container_workdir,
217
+ self.config.container_image,
218
+ "python",
219
+ "-m",
220
+ "pytest",
221
+ target,
222
+ *extra_args,
223
+ ]
224
+
225
+ def _host_pytest_command(self, *, path: Path, extra_args: list[str]) -> list[str]:
226
+ configured = str(self.config.pytest_bin).strip() or "pytest"
227
+ configured_path = Path(configured).expanduser()
228
+ if configured_path.is_absolute() and configured_path.exists():
229
+ return [str(configured_path), str(path), *extra_args]
230
+ if any(sep in configured for sep in ("/", "\\")):
231
+ repo_relative = (self.repo_root / configured_path).resolve()
232
+ if repo_relative.exists():
233
+ return [str(repo_relative), str(path), *extra_args]
234
+ if configured_path.exists():
235
+ return [str(configured_path.resolve()), str(path), *extra_args]
236
+ elif shutil.which(configured):
237
+ return [configured, str(path), *extra_args]
238
+ if importlib.util.find_spec("pytest") is not None:
239
+ return [sys.executable, "-m", "pytest", str(path), *extra_args]
240
+ raise ValueError(
241
+ f"Configured pytest_bin '{configured}' is unavailable and fallback 'python -m pytest' is not installed."
242
+ )
243
+
244
+ def _container_suite_path(self, path: Path, suite_path: str) -> str:
245
+ try:
246
+ return str(path.resolve().relative_to(self.repo_root))
247
+ except ValueError as exc:
248
+ raise ValueError(
249
+ f"Container invariant path is outside repo root: {path.resolve()} (root: {self.repo_root})"
250
+ ) from exc
251
+
252
+
253
+ def _invariant_diagnostic(result: InvariantCaseResult) -> dict[str, Any]:
254
+ if result.status == "missing":
255
+ return {"evidence_found": [result.test_path], "evidence_expected": ["configured invariant test path to exist"], "gap_description": f"Invariant path '{result.test_path}' was missing.", "gap_characterization": "comprehension_gap", "distance_signal": "far"}
256
+ if result.status == "error":
257
+ return {"evidence_found": [result.stderr[:200]] if result.stderr else [result.test_path], "evidence_expected": [f"invariant '{result.test_path}' to run successfully"], "gap_description": f"Invariant '{result.test_path}' could not execute cleanly.", "gap_characterization": "execution_gap", "distance_signal": "moderate"}
258
+ return {"evidence_found": [result.test_path], "evidence_expected": [f"invariant '{result.test_path}' to pass"], "gap_description": f"Invariant '{result.test_path}' failed.", "gap_characterization": "execution_gap", "distance_signal": "close"}
cortex/packs.py ADDED
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import tomllib
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ PACK_SCHEMA_VERSION = "cortex_pack_v1"
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class PackOverlay:
13
+ challenge_categories: list[str] = field(default_factory=list)
14
+ invariant_paths: list[str] = field(default_factory=list)
15
+ blocked_tools: list[str] = field(default_factory=list)
16
+ allowed_tools: list[str] = field(default_factory=list)
17
+ warnings: list[str] = field(default_factory=list)
18
+
19
+
20
+ def load_pack_overlay(*, root: Path, pack_paths: list[str]) -> PackOverlay:
21
+ overlay = PackOverlay()
22
+ for raw_path in pack_paths:
23
+ declared_path = str(raw_path).strip()
24
+ if not declared_path:
25
+ continue
26
+ manifest_path = (
27
+ Path(declared_path).expanduser()
28
+ if Path(declared_path).is_absolute()
29
+ else (root / declared_path).expanduser()
30
+ ).resolve()
31
+ if not manifest_path.exists():
32
+ overlay.warnings.append(f"Pack manifest not found: {declared_path}")
33
+ continue
34
+ try:
35
+ with manifest_path.open("rb") as fh:
36
+ payload = tomllib.load(fh)
37
+ except Exception as exc: # noqa: BLE001
38
+ overlay.warnings.append(f"Pack manifest parse error in {manifest_path}: {exc}")
39
+ continue
40
+ if not isinstance(payload, dict):
41
+ overlay.warnings.append(f"Pack manifest must be a TOML table: {manifest_path}")
42
+ continue
43
+
44
+ schema = str(payload.get("schema") or "").strip()
45
+ if schema != PACK_SCHEMA_VERSION:
46
+ overlay.warnings.append(
47
+ f"Unsupported pack schema in {manifest_path}: {schema or 'missing'} "
48
+ f"(expected {PACK_SCHEMA_VERSION})"
49
+ )
50
+ continue
51
+
52
+ pack_id = str(payload.get("pack_id") or manifest_path.stem).strip() or manifest_path.stem
53
+ categories = _as_str_list(payload.get("challenge_categories"))
54
+ invariants = _as_str_list(payload.get("invariant_paths"))
55
+ blocked_tools = _as_str_list(payload.get("blocked_tools"))
56
+ allowed_tools = _as_str_list(payload.get("allowed_tools"))
57
+ if not (categories or invariants or blocked_tools or allowed_tools):
58
+ overlay.warnings.append(f"Pack '{pack_id}' declares no challenges/invariants/blocklist entries.")
59
+ continue
60
+
61
+ overlay.challenge_categories.extend(categories)
62
+ overlay.invariant_paths.extend(
63
+ _resolve_invariant_paths(
64
+ repo_root=root,
65
+ manifest_dir=manifest_path.parent,
66
+ invariant_paths=invariants,
67
+ )
68
+ )
69
+ overlay.blocked_tools.extend(blocked_tools)
70
+ overlay.allowed_tools.extend(allowed_tools)
71
+
72
+ overlay.challenge_categories = _unique_nonempty(overlay.challenge_categories)
73
+ overlay.invariant_paths = _unique_nonempty(overlay.invariant_paths)
74
+ overlay.blocked_tools = _unique_nonempty(overlay.blocked_tools)
75
+ overlay.allowed_tools = _unique_nonempty(overlay.allowed_tools)
76
+ overlay.warnings = _unique_nonempty(overlay.warnings)
77
+ return overlay
78
+
79
+
80
+ def _resolve_invariant_paths(
81
+ *,
82
+ repo_root: Path,
83
+ manifest_dir: Path,
84
+ invariant_paths: list[str],
85
+ ) -> list[str]:
86
+ resolved: list[str] = []
87
+ for value in invariant_paths:
88
+ path = Path(value)
89
+ if path.is_absolute():
90
+ candidate = path.resolve()
91
+ else:
92
+ # Prefer repo-root-relative paths so pack manifests can be dropped into a project
93
+ # without requiring path rewrites for test suites that already use repo-relative layout.
94
+ root_candidate = (repo_root / path).resolve()
95
+ manifest_candidate = (manifest_dir / path).resolve()
96
+ candidate = root_candidate if (root_candidate.exists() or not manifest_candidate.exists()) else manifest_candidate
97
+ try:
98
+ candidate = candidate.relative_to(repo_root)
99
+ except ValueError:
100
+ pass
101
+ resolved.append(candidate.as_posix() if isinstance(candidate, Path) else str(candidate))
102
+ return resolved
103
+
104
+
105
+ def _as_str_list(value: Any) -> list[str]:
106
+ return [str(v).strip() for v in value] if isinstance(value, list) else []
107
+
108
+
109
+ def _unique_nonempty(values: list[str]) -> list[str]:
110
+ seen: set[str] = set()
111
+ result: list[str] = []
112
+ for item in values:
113
+ token = str(item).strip()
114
+ if not token or token in seen:
115
+ continue
116
+ seen.add(token)
117
+ result.append(token)
118
+ return result
cortex/repomap.py ADDED
@@ -0,0 +1,6 @@
1
+ import sys
2
+ from importlib import import_module
3
+ from types import ModuleType
4
+
5
+ _IMPL = import_module("cortex_repomap.engine")
6
+ sys.modules[__name__].__class__ = type("_ShimModule", (ModuleType,), {"__getattr__": lambda self, name: getattr(_IMPL, name), "__setattr__": lambda self, name, value: (setattr(_IMPL, name, value), ModuleType.__setattr__(self, name, value))[1]})