crprotocol 2.0.0__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 (153) hide show
  1. crp/__init__.py +126 -0
  2. crp/__main__.py +8 -0
  3. crp/_typing.py +27 -0
  4. crp/_version.py +5 -0
  5. crp/adapters.py +31 -0
  6. crp/advanced/__init__.py +40 -0
  7. crp/advanced/auto_ingest.py +400 -0
  8. crp/advanced/cqs.py +235 -0
  9. crp/advanced/cross_window.py +477 -0
  10. crp/advanced/curator.py +265 -0
  11. crp/advanced/feedback.py +146 -0
  12. crp/advanced/hierarchical.py +211 -0
  13. crp/advanced/meta_learning.py +401 -0
  14. crp/advanced/parallel.py +98 -0
  15. crp/advanced/review_cycle.py +329 -0
  16. crp/advanced/scale_mode.py +129 -0
  17. crp/advanced/source_grounding.py +207 -0
  18. crp/ckf/__init__.py +35 -0
  19. crp/ckf/community.py +377 -0
  20. crp/ckf/fabric.py +445 -0
  21. crp/ckf/gc.py +175 -0
  22. crp/ckf/graph_walk.py +87 -0
  23. crp/ckf/merge.py +133 -0
  24. crp/ckf/pattern_query.py +122 -0
  25. crp/ckf/pubsub.py +128 -0
  26. crp/ckf/semantic.py +207 -0
  27. crp/cli/__init__.py +7 -0
  28. crp/cli/main.py +329 -0
  29. crp/cli/sidecar.py +929 -0
  30. crp/cli/startup.py +272 -0
  31. crp/continuation/__init__.py +103 -0
  32. crp/continuation/completion.py +348 -0
  33. crp/continuation/degradation.py +157 -0
  34. crp/continuation/document_map.py +160 -0
  35. crp/continuation/flow.py +109 -0
  36. crp/continuation/gap.py +419 -0
  37. crp/continuation/manager.py +484 -0
  38. crp/continuation/quality_monitor.py +179 -0
  39. crp/continuation/stitch.py +419 -0
  40. crp/continuation/trigger.py +142 -0
  41. crp/continuation/voice.py +157 -0
  42. crp/core/__init__.py +69 -0
  43. crp/core/batch.py +77 -0
  44. crp/core/circuit_breaker.py +116 -0
  45. crp/core/config.py +377 -0
  46. crp/core/context_tools.py +540 -0
  47. crp/core/dispatch_router.py +3977 -0
  48. crp/core/errors.py +128 -0
  49. crp/core/extraction_facade.py +384 -0
  50. crp/core/facilitator.py +713 -0
  51. crp/core/idempotency.py +215 -0
  52. crp/core/orchestrator.py +1435 -0
  53. crp/core/relay_strategies.py +613 -0
  54. crp/core/security_manager.py +140 -0
  55. crp/core/session.py +134 -0
  56. crp/core/task_intent.py +36 -0
  57. crp/core/window.py +363 -0
  58. crp/envelope/__init__.py +30 -0
  59. crp/envelope/builder.py +288 -0
  60. crp/envelope/decomposer.py +236 -0
  61. crp/envelope/formatter.py +168 -0
  62. crp/envelope/packer.py +211 -0
  63. crp/envelope/reranker.py +209 -0
  64. crp/envelope/scoring.py +310 -0
  65. crp/extraction/__init__.py +45 -0
  66. crp/extraction/complexity.py +96 -0
  67. crp/extraction/contradiction.py +132 -0
  68. crp/extraction/pipeline.py +360 -0
  69. crp/extraction/quality_gate.py +237 -0
  70. crp/extraction/stage1_regex.py +173 -0
  71. crp/extraction/stage2_statistical.py +244 -0
  72. crp/extraction/stage3_gliner.py +210 -0
  73. crp/extraction/stage4_uie.py +183 -0
  74. crp/extraction/stage5_discourse.py +175 -0
  75. crp/extraction/stage6_llm.py +178 -0
  76. crp/extraction/structured_output.py +219 -0
  77. crp/extraction/types.py +299 -0
  78. crp/license_guard.py +722 -0
  79. crp/observability/__init__.py +30 -0
  80. crp/observability/audit.py +118 -0
  81. crp/observability/events.py +233 -0
  82. crp/observability/metrics.py +264 -0
  83. crp/observability/quality.py +135 -0
  84. crp/observability/structured_logging.py +81 -0
  85. crp/observability/telemetry.py +117 -0
  86. crp/provenance/__init__.py +314 -0
  87. crp/provenance/_embeddings.py +97 -0
  88. crp/provenance/_types.py +378 -0
  89. crp/provenance/attribution_scorer.py +252 -0
  90. crp/provenance/claim_detector.py +229 -0
  91. crp/provenance/contradiction_detector.py +243 -0
  92. crp/provenance/distortion_detector.py +397 -0
  93. crp/provenance/entailment_verifier.py +358 -0
  94. crp/provenance/fabrication_detector.py +203 -0
  95. crp/provenance/hallucination_scorer.py +320 -0
  96. crp/provenance/omission_analyzer.py +106 -0
  97. crp/provenance/provenance_chain.py +205 -0
  98. crp/provenance/report_generator.py +440 -0
  99. crp/providers/__init__.py +43 -0
  100. crp/providers/anthropic.py +270 -0
  101. crp/providers/base.py +135 -0
  102. crp/providers/custom.py +63 -0
  103. crp/providers/diagnostic.py +251 -0
  104. crp/providers/llamacpp.py +224 -0
  105. crp/providers/manager.py +139 -0
  106. crp/providers/ollama.py +243 -0
  107. crp/providers/openai.py +628 -0
  108. crp/providers/tokenizers.py +48 -0
  109. crp/py.typed +0 -0
  110. crp/resources/__init__.py +53 -0
  111. crp/resources/adaptive_allocator.py +525 -0
  112. crp/resources/cost_model.py +388 -0
  113. crp/resources/overhead_manager.py +217 -0
  114. crp/resources/resource_manager.py +262 -0
  115. crp/schemas/__init__.py +20 -0
  116. crp/schemas/cost-estimate.json +33 -0
  117. crp/schemas/crp-error.json +43 -0
  118. crp/schemas/envelope-preview.json +40 -0
  119. crp/schemas/persisted-state-header.json +27 -0
  120. crp/schemas/quality-report.json +94 -0
  121. crp/schemas/session-handle.json +33 -0
  122. crp/schemas/session-status.json +57 -0
  123. crp/schemas/stream-event.json +18 -0
  124. crp/schemas/task-intent.json +42 -0
  125. crp/security/__init__.py +93 -0
  126. crp/security/audit_trail.py +392 -0
  127. crp/security/binding.py +192 -0
  128. crp/security/compliance.py +813 -0
  129. crp/security/consent.py +593 -0
  130. crp/security/embedding_defense.py +161 -0
  131. crp/security/encryption.py +202 -0
  132. crp/security/injection.py +335 -0
  133. crp/security/integrity.py +267 -0
  134. crp/security/privacy.py +662 -0
  135. crp/security/quarantine.py +249 -0
  136. crp/security/rbac.py +221 -0
  137. crp/security/validation.py +164 -0
  138. crp/state/__init__.py +31 -0
  139. crp/state/cold_storage.py +258 -0
  140. crp/state/compaction.py +263 -0
  141. crp/state/critical_state.py +104 -0
  142. crp/state/event_log.py +313 -0
  143. crp/state/fact.py +189 -0
  144. crp/state/serialization.py +189 -0
  145. crp/state/session_cleanup.py +77 -0
  146. crp/state/snapshot.py +290 -0
  147. crp/state/warm_store.py +346 -0
  148. crprotocol-2.0.0.dist-info/METADATA +1295 -0
  149. crprotocol-2.0.0.dist-info/RECORD +153 -0
  150. crprotocol-2.0.0.dist-info/WHEEL +4 -0
  151. crprotocol-2.0.0.dist-info/entry_points.txt +2 -0
  152. crprotocol-2.0.0.dist-info/licenses/LICENSE.md +170 -0
  153. crprotocol-2.0.0.dist-info/licenses/NOTICE +18 -0
@@ -0,0 +1,262 @@
1
+ # Copyright © 2025 Constantinos Vidiniotis. All rights reserved.
2
+ # Licensed under Elastic License 2.0 — see LICENSE.md for details.
3
+ """Centralized resource manager — memory tracking, model registry, pressure (§audit R1).
4
+
5
+ Tracks memory consumption, ML model lifecycle, session budgets,
6
+ and provides pressure signals for adaptive dispatch behavior.
7
+
8
+ Pressure levels:
9
+ "none" : usage < 50% of budget → normal operation
10
+ "low" : 50-70% → compaction eligible
11
+ "medium" : 70-85% → aggressive compaction, idle model unload
12
+ "high" : 85-95% → forced compaction, model unload, GC
13
+ "critical" : >95% → emergency cleanup, session pruning
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import gc
19
+ import logging
20
+ import os
21
+ import time
22
+ from dataclasses import dataclass, field
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Budget defaults
28
+ # ---------------------------------------------------------------------------
29
+
30
+ DEFAULT_MEMORY_BUDGET_MB = 512
31
+ PRESSURE_THRESHOLDS: list[tuple[str, float]] = [
32
+ ("critical", 0.95),
33
+ ("high", 0.85),
34
+ ("medium", 0.70),
35
+ ("low", 0.50),
36
+ ("none", 0.0),
37
+ ]
38
+
39
+ # Estimated per-model memory footprint (MB).
40
+ MODEL_ESTIMATES: dict[str, float] = {
41
+ "sentence-transformers": 150.0,
42
+ "cross-encoder": 200.0,
43
+ "gliner": 200.0,
44
+ "uie": 400.0,
45
+ "hnsw-index": 50.0, # varies with fact count
46
+ }
47
+
48
+ # Average bytes per fact in warm store (empirical).
49
+ _BYTES_PER_FACT = 500
50
+
51
+
52
+ @dataclass
53
+ class ResourceSnapshot:
54
+ """Point-in-time resource utilization snapshot."""
55
+
56
+ timestamp: float = 0.0
57
+ process_rss_mb: float = 0.0
58
+ crp_estimated_mb: float = 0.0
59
+ model_memory_mb: float = 0.0
60
+ fact_store_mb: float = 0.0
61
+ budget_mb: float = DEFAULT_MEMORY_BUDGET_MB
62
+ pressure_level: str = "none"
63
+ models_loaded: list[str] = field(default_factory=list)
64
+ fact_count: int = 0
65
+ session_count: int = 0
66
+
67
+ @property
68
+ def utilization_ratio(self) -> float:
69
+ if self.budget_mb <= 0:
70
+ return 0.0
71
+ return self.crp_estimated_mb / self.budget_mb
72
+
73
+
74
+ @dataclass
75
+ class ModelEntry:
76
+ """Registered ML model with lifecycle tracking."""
77
+
78
+ name: str
79
+ estimated_mb: float
80
+ loaded_at: float = 0.0
81
+ last_used_at: float = 0.0
82
+ use_count: int = 0
83
+ is_loaded: bool = False
84
+
85
+
86
+ class ResourceManager:
87
+ """Centralized resource tracking and pressure management.
88
+
89
+ Usage::
90
+
91
+ rm = ResourceManager(budget_mb=512)
92
+ rm.register_model("sentence-transformers", 150)
93
+ rm.mark_model_loaded("sentence-transformers")
94
+ snap = rm.snapshot()
95
+ print(snap.pressure_level)
96
+ """
97
+
98
+ def __init__(self, budget_mb: float = DEFAULT_MEMORY_BUDGET_MB) -> None:
99
+ self._budget_mb = budget_mb
100
+ self._models: dict[str, ModelEntry] = {}
101
+ self._fact_count = 0
102
+ self._session_count = 0
103
+ self._created_at = time.time()
104
+ self._last_gc = 0.0
105
+ self._cleanup_count = 0
106
+
107
+ # -- Model lifecycle ---------------------------------------------------
108
+
109
+ def register_model(self, name: str, estimated_mb: float) -> None:
110
+ """Register an ML model (call at init, before loading)."""
111
+ self._models[name] = ModelEntry(name=name, estimated_mb=estimated_mb)
112
+
113
+ def mark_model_loaded(self, name: str) -> None:
114
+ """Mark a model as loaded into memory."""
115
+ if name in self._models:
116
+ self._models[name].is_loaded = True
117
+ self._models[name].loaded_at = time.time()
118
+ self._models[name].last_used_at = time.time()
119
+
120
+ def mark_model_used(self, name: str) -> None:
121
+ """Record model usage (resets idle timer)."""
122
+ if name in self._models:
123
+ self._models[name].last_used_at = time.time()
124
+ self._models[name].use_count += 1
125
+
126
+ def mark_model_unloaded(self, name: str) -> None:
127
+ """Mark a model as unloaded from memory."""
128
+ if name in self._models:
129
+ self._models[name].is_loaded = False
130
+
131
+ # -- Fact store tracking -----------------------------------------------
132
+
133
+ def update_fact_count(self, count: int) -> None:
134
+ self._fact_count = count
135
+
136
+ def update_session_count(self, count: int) -> None:
137
+ self._session_count = count
138
+
139
+ # -- Snapshot & pressure -----------------------------------------------
140
+
141
+ def snapshot(self) -> ResourceSnapshot:
142
+ """Take a point-in-time resource snapshot."""
143
+ model_mb = sum(m.estimated_mb for m in self._models.values() if m.is_loaded)
144
+ fact_mb = self._fact_count * _BYTES_PER_FACT / (1024 * 1024)
145
+ crp_mb = model_mb + fact_mb
146
+
147
+ rss_mb = _get_process_rss_mb()
148
+ pressure = self._compute_pressure(crp_mb)
149
+ loaded = [m.name for m in self._models.values() if m.is_loaded]
150
+
151
+ return ResourceSnapshot(
152
+ timestamp=time.time(),
153
+ process_rss_mb=round(rss_mb, 1),
154
+ crp_estimated_mb=round(crp_mb, 1),
155
+ model_memory_mb=round(model_mb, 1),
156
+ fact_store_mb=round(fact_mb, 3),
157
+ budget_mb=self._budget_mb,
158
+ pressure_level=pressure,
159
+ models_loaded=loaded,
160
+ fact_count=self._fact_count,
161
+ session_count=self._session_count,
162
+ )
163
+
164
+ def _compute_pressure(self, crp_mb: float) -> str:
165
+ if self._budget_mb <= 0:
166
+ return "none"
167
+ ratio = crp_mb / self._budget_mb
168
+ for name, threshold in PRESSURE_THRESHOLDS:
169
+ if ratio >= threshold:
170
+ return name
171
+ return "none"
172
+
173
+ # -- Cleanup actions ---------------------------------------------------
174
+
175
+ def should_cleanup(self) -> bool:
176
+ snap = self.snapshot()
177
+ return snap.pressure_level in ("medium", "high", "critical")
178
+
179
+ def run_gc(self) -> int:
180
+ collected = gc.collect()
181
+ self._last_gc = time.time()
182
+ self._cleanup_count += 1
183
+ logger.debug("GC collected %d objects (run #%d)", collected, self._cleanup_count)
184
+ return collected
185
+
186
+ def get_idle_models(self, idle_seconds: float = 300.0) -> list[str]:
187
+ now = time.time()
188
+ return [
189
+ m.name for m in self._models.values()
190
+ if m.is_loaded and (now - m.last_used_at) > idle_seconds
191
+ ]
192
+
193
+ def mark_unloaded(self, model_name: str) -> None:
194
+ """Mark a model as unloaded (freed from memory)."""
195
+ if model_name in self._models:
196
+ self._models[model_name].is_loaded = False
197
+ logger.info("Model marked unloaded: %s", model_name)
198
+
199
+ def trigger_gc(self) -> int:
200
+ """Run GC if pressure warrants it (alias for should_cleanup + run_gc)."""
201
+ if self.should_cleanup():
202
+ return self.run_gc()
203
+ return 0
204
+
205
+ # -- Summary -----------------------------------------------------------
206
+
207
+ def summary(self) -> dict:
208
+ snap = self.snapshot()
209
+ return {
210
+ "budget_mb": self._budget_mb,
211
+ "crp_estimated_mb": snap.crp_estimated_mb,
212
+ "model_memory_mb": snap.model_memory_mb,
213
+ "fact_store_mb": snap.fact_store_mb,
214
+ "process_rss_mb": snap.process_rss_mb,
215
+ "pressure_level": snap.pressure_level,
216
+ "utilization_pct": round(snap.utilization_ratio * 100, 1),
217
+ "models_loaded": snap.models_loaded,
218
+ "fact_count": snap.fact_count,
219
+ "gc_runs": self._cleanup_count,
220
+ }
221
+
222
+
223
+ # ---------------------------------------------------------------------------
224
+ # Helpers
225
+ # ---------------------------------------------------------------------------
226
+
227
+ def _get_process_rss_mb() -> float:
228
+ """Get process RSS in MB (best-effort, returns 0 on failure)."""
229
+ try:
230
+ if os.name == "nt":
231
+ import ctypes
232
+ import ctypes.wintypes
233
+
234
+ class PROCESS_MEMORY_COUNTERS(ctypes.Structure):
235
+ _fields_ = [
236
+ ("cb", ctypes.wintypes.DWORD),
237
+ ("PageFaultCount", ctypes.wintypes.DWORD),
238
+ ("PeakWorkingSetSize", ctypes.c_size_t),
239
+ ("WorkingSetSize", ctypes.c_size_t),
240
+ ("QuotaPeakPagedPoolUsage", ctypes.c_size_t),
241
+ ("QuotaPagedPoolUsage", ctypes.c_size_t),
242
+ ("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t),
243
+ ("QuotaNonPagedPoolUsage", ctypes.c_size_t),
244
+ ("PagefileUsage", ctypes.c_size_t),
245
+ ("PeakPagefileUsage", ctypes.c_size_t),
246
+ ]
247
+
248
+ pmc = PROCESS_MEMORY_COUNTERS()
249
+ pmc.cb = ctypes.sizeof(pmc)
250
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
251
+ handle = kernel32.GetCurrentProcess()
252
+ psapi = ctypes.windll.psapi # type: ignore[attr-defined]
253
+ if psapi.GetProcessMemoryInfo(handle, ctypes.byref(pmc), pmc.cb):
254
+ return pmc.WorkingSetSize / (1024 * 1024)
255
+ else:
256
+ with open("/proc/self/status") as f:
257
+ for line in f:
258
+ if line.startswith("VmRSS:"):
259
+ return int(line.split()[1]) / 1024
260
+ except Exception:
261
+ pass
262
+ return 0.0
@@ -0,0 +1,20 @@
1
+ # Copyright © 2025 Constantinos Vidiniotis. All rights reserved.
2
+ # Licensed under Elastic License 2.0 — see LICENSE.md for details.
3
+ """JSON Schema loader for CRP data contracts."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from importlib import resources
9
+ from typing import Any
10
+
11
+
12
+ def load_schema(name: str) -> dict[str, Any]:
13
+ """Load a JSON Schema by name (without .json extension).
14
+
15
+ >>> schema = load_schema("task-intent")
16
+ >>> schema["type"]
17
+ 'object'
18
+ """
19
+ ref = resources.files("crp.schemas").joinpath(f"{name}.json")
20
+ return json.loads(ref.read_text(encoding="utf-8")) # type: ignore[arg-type]
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CostEstimate",
4
+ "description": "Returned by estimate_session(). Provides pre-flight cost estimation without performing any LLM calls.",
5
+ "type": "object",
6
+ "required": ["estimated_windows", "estimated_input_tokens", "estimated_output_tokens"],
7
+ "properties": {
8
+ "estimated_windows": {
9
+ "type": "integer",
10
+ "minimum": 1,
11
+ "description": "Estimated total windows (dispatches + continuations)."
12
+ },
13
+ "estimated_input_tokens": {
14
+ "type": "integer",
15
+ "minimum": 0,
16
+ "description": "Estimated cumulative input tokens across all windows."
17
+ },
18
+ "estimated_output_tokens": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Estimated cumulative output tokens across all windows."
22
+ },
23
+ "estimated_cost_usd": {
24
+ "type": ["number", "null"],
25
+ "description": "Estimated cost in USD. NULL if provider pricing not supplied."
26
+ },
27
+ "confidence": {
28
+ "type": "string",
29
+ "enum": ["high", "medium", "low"],
30
+ "description": "Estimation confidence. 'high' = known task pattern, 'low' = novel task."
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CRPError",
4
+ "description": "Standard error format for all CRP operations. Error codes are inspired by JSON-RPC 2.0 and gRPC status codes.",
5
+ "type": "object",
6
+ "required": ["code", "message", "details"],
7
+ "properties": {
8
+ "code": {
9
+ "type": "integer",
10
+ "minimum": 1001,
11
+ "maximum": 1031,
12
+ "description": "Error code from the CRP error taxonomy (§6.10.4). Range: 1001-1031."
13
+ },
14
+ "message": {
15
+ "type": "string",
16
+ "description": "Human-readable error description."
17
+ },
18
+ "details": {
19
+ "type": "object",
20
+ "description": "Error-specific structured diagnostic data."
21
+ }
22
+ },
23
+ "$defs": {
24
+ "ErrorCodes": {
25
+ "description": "CRP error code reference",
26
+ "enum": [
27
+ { "const": 1001, "title": "BudgetExhaustedError", "description": "Any cost cap (windows, tokens, rate) exceeded" },
28
+ { "const": 1002, "title": "RateLimitExceeded", "description": "Per-session rate limit hit" },
29
+ { "const": 1003, "title": "SessionExpired", "description": "Session timeout reached" },
30
+ { "const": 1004, "title": "SessionLimitExceeded", "description": "Max concurrent sessions reached" },
31
+ { "const": 1005, "title": "SessionClosed", "description": "Operation on closed session" },
32
+ { "const": 1010, "title": "ValidationError", "description": "Structural validation failure (§22.3.1)" },
33
+ { "const": 1011, "title": "SecurityInvariantError", "description": "Attempted violation of security invariant" },
34
+ { "const": 1012, "title": "SignatureInvalid", "description": "Request HMAC signature verification failed" },
35
+ { "const": 1013, "title": "RBACDenied", "description": "Operation not permitted for current role" },
36
+ { "const": 1020, "title": "ProviderError", "description": "LLM provider returned an error" },
37
+ { "const": 1021, "title": "ProviderTimeout", "description": "LLM provider did not respond within timeout" },
38
+ { "const": 1030, "title": "StateCorrupted", "description": "Cold state integrity verification failed" },
39
+ { "const": 1031, "title": "ChainVerificationFailed", "description": "Fact chain signature verification failed" }
40
+ ]
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "EnvelopePreview",
4
+ "description": "Returned by preview_envelope(). Inspect what the envelope would contain without dispatching.",
5
+ "type": "object",
6
+ "required": ["total_tokens", "envelope_tokens", "facts_included", "saturation"],
7
+ "properties": {
8
+ "total_tokens": {
9
+ "type": "integer",
10
+ "minimum": 0,
11
+ "description": "Total tokens (system_prompt + envelope + task_input + generation_reserve)."
12
+ },
13
+ "envelope_tokens": {
14
+ "type": "integer",
15
+ "minimum": 0,
16
+ "description": "Tokens consumed by the envelope content."
17
+ },
18
+ "generation_reserve": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Tokens reserved for LLM generation output."
22
+ },
23
+ "facts_included": {
24
+ "type": "integer",
25
+ "minimum": 0,
26
+ "description": "Number of facts included in the envelope."
27
+ },
28
+ "facts_available": {
29
+ "type": "integer",
30
+ "minimum": 0,
31
+ "description": "Total facts available in warm + cold state."
32
+ },
33
+ "saturation": {
34
+ "type": "number",
35
+ "minimum": 0.0,
36
+ "maximum": 1.0,
37
+ "description": "Fraction of available envelope space used (0.0–1.0)."
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "PersistedStateHeader",
4
+ "description": "Header for all persisted CRP data structures (cold state, event log, exported state). Enables schema migration across versions.",
5
+ "type": "object",
6
+ "required": ["schema_version", "protocol_version", "created_at", "checksum"],
7
+ "properties": {
8
+ "schema_version": {
9
+ "type": "string",
10
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
11
+ "description": "SemVer for the persisted format (e.g., '2.0.0'). Implementations MUST read state from the same Major version."
12
+ },
13
+ "protocol_version": {
14
+ "type": "string",
15
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
16
+ "description": "CRP protocol version that wrote this state."
17
+ },
18
+ "created_at": {
19
+ "type": "number",
20
+ "description": "Timestamp of state creation (seconds since epoch)."
21
+ },
22
+ "checksum": {
23
+ "type": "string",
24
+ "description": "BLAKE3 integrity hash of the payload. If verification fails, implementations MUST raise StateCorrupted (code 1030)."
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "QualityReport",
4
+ "description": "Returned by dispatch() alongside the LLM output. MUST always be provided.",
5
+ "type": "object",
6
+ "required": ["session_id", "window_id", "output", "facts_extracted", "security_flags"],
7
+ "properties": {
8
+ "session_id": {
9
+ "type": "string",
10
+ "format": "uuid",
11
+ "description": "UUID v4 identifying the session. MUST be stable across all windows in a session."
12
+ },
13
+ "window_id": {
14
+ "type": "string",
15
+ "format": "uuid",
16
+ "description": "UUID v4 identifying this specific window invocation."
17
+ },
18
+ "output": {
19
+ "type": "string",
20
+ "description": "The complete, unmodified LLM output (Axiom 9 — Output Integrity). MUST NOT be filtered, summarized, or truncated by CRP."
21
+ },
22
+ "facts_extracted": {
23
+ "type": "integer",
24
+ "minimum": 0,
25
+ "description": "Number of facts extracted from this window's output."
26
+ },
27
+ "continuation_windows": {
28
+ "type": "integer",
29
+ "minimum": 0,
30
+ "description": "Number of continuation windows triggered (0 if output fit in one window)."
31
+ },
32
+ "envelope_saturation": {
33
+ "type": "number",
34
+ "minimum": 0.0,
35
+ "maximum": 1.0,
36
+ "description": "Fraction of available envelope space used (0.0–1.0). SHOULD be > 0.9 for non-trivial tasks."
37
+ },
38
+ "quality_tier": {
39
+ "type": "string",
40
+ "enum": ["S", "A", "B", "C", "D"],
41
+ "description": "CRP quality tier for this task based on window count (§10)."
42
+ },
43
+ "security_flags": {
44
+ "$ref": "#/$defs/SecurityFlags",
45
+ "description": "Security observations for this window. MUST always be present."
46
+ },
47
+ "telemetry": {
48
+ "type": ["object", "null"],
49
+ "description": "Performance telemetry. MAY be omitted if telemetry is disabled."
50
+ }
51
+ },
52
+ "$defs": {
53
+ "SecurityFlags": {
54
+ "type": "object",
55
+ "properties": {
56
+ "injection_markers_detected": {
57
+ "type": "integer",
58
+ "minimum": 0,
59
+ "description": "Count of injection pattern matches detected in input."
60
+ },
61
+ "injection_marker_details": {
62
+ "type": "array",
63
+ "items": {
64
+ "type": "object",
65
+ "properties": {
66
+ "offset": { "type": "integer", "description": "Character offset of the match." },
67
+ "pattern_name": { "type": "string", "description": "Name of the matched injection pattern." },
68
+ "matched_text": { "type": "string", "description": "The text that matched the pattern." }
69
+ }
70
+ },
71
+ "description": "Details of each injection pattern match."
72
+ },
73
+ "unicode_normalized": {
74
+ "type": "boolean",
75
+ "description": "Whether Unicode normalization was applied to input."
76
+ },
77
+ "control_chars_stripped": {
78
+ "type": "integer",
79
+ "minimum": 0,
80
+ "description": "Count of control characters removed from input."
81
+ },
82
+ "input_truncated": {
83
+ "type": "boolean",
84
+ "description": "Whether input was truncated to fit max_task_input_bytes."
85
+ },
86
+ "integrity_violations": {
87
+ "type": "integer",
88
+ "minimum": 0,
89
+ "description": "Count of fact integrity chain verification failures."
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "SessionHandle",
4
+ "description": "Returned by init(). Note: session_key is NEVER included in JSON serialization — it exists only in memory.",
5
+ "type": "object",
6
+ "required": ["session_id", "protocol_version", "capabilities", "created_at", "expires_at"],
7
+ "properties": {
8
+ "session_id": {
9
+ "type": "string",
10
+ "format": "uuid",
11
+ "description": "UUID v4 — unique per session."
12
+ },
13
+ "protocol_version": {
14
+ "type": "string",
15
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
16
+ "description": "SemVer protocol version (e.g., '2.0.0')."
17
+ },
18
+ "capabilities": {
19
+ "type": "array",
20
+ "items": { "type": "string" },
21
+ "uniqueItems": true,
22
+ "description": "Granted RBAC permissions (e.g., 'dispatch', 'configure', 'session_status')."
23
+ },
24
+ "created_at": {
25
+ "type": "number",
26
+ "description": "Monotonic timestamp (seconds since epoch) of session creation."
27
+ },
28
+ "expires_at": {
29
+ "type": "number",
30
+ "description": "Session expiry timestamp (seconds since epoch)."
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "SessionStatus",
4
+ "description": "Returned by session_status(). MUST always be available for OBSERVER role or above.",
5
+ "type": "object",
6
+ "required": ["session_id", "windows_completed", "total_input_tokens", "total_output_tokens", "facts_in_warm_state", "overhead_ratio"],
7
+ "properties": {
8
+ "session_id": {
9
+ "type": "string",
10
+ "format": "uuid",
11
+ "description": "UUID v4 identifying the session."
12
+ },
13
+ "windows_completed": {
14
+ "type": "integer",
15
+ "minimum": 0,
16
+ "description": "Total windows completed in this session (including continuations)."
17
+ },
18
+ "total_input_tokens": {
19
+ "type": "integer",
20
+ "minimum": 0,
21
+ "description": "Cumulative input tokens sent to the LLM across all windows."
22
+ },
23
+ "total_output_tokens": {
24
+ "type": "integer",
25
+ "minimum": 0,
26
+ "description": "Cumulative output tokens received from the LLM across all windows."
27
+ },
28
+ "facts_in_warm_state": {
29
+ "type": "integer",
30
+ "minimum": 0,
31
+ "description": "Current fact count in warm state (Tier 2)."
32
+ },
33
+ "overhead_ratio": {
34
+ "type": "number",
35
+ "minimum": 0.0,
36
+ "description": "Current overhead windows / productive windows ratio."
37
+ },
38
+ "remaining_budget": {
39
+ "type": ["object", "null"],
40
+ "description": "Remaining budget against cost caps. NULL if no caps set.",
41
+ "properties": {
42
+ "windows_remaining": {
43
+ "type": ["integer", "null"],
44
+ "description": "Windows remaining before max_windows_per_session cap."
45
+ },
46
+ "input_tokens_remaining": {
47
+ "type": ["integer", "null"],
48
+ "description": "Input tokens remaining before max_total_input_tokens cap."
49
+ },
50
+ "output_tokens_remaining": {
51
+ "type": ["integer", "null"],
52
+ "description": "Output tokens remaining before max_total_output_tokens cap."
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "StreamEvent",
4
+ "description": "Events emitted by dispatch_stream(). The stream MUST emit exactly one 'done' event as the final event (or one 'error').",
5
+ "type": "object",
6
+ "required": ["event_type", "data"],
7
+ "properties": {
8
+ "event_type": {
9
+ "type": "string",
10
+ "enum": ["token", "extraction", "continuation", "window_complete", "done", "error"],
11
+ "description": "Discriminator for the event type. Concatenating all 'token' data values MUST produce the same string as non-streaming dispatch() output."
12
+ },
13
+ "data": {
14
+ "description": "Event payload. Type depends on event_type: token=string, extraction=ExtractionProgress, continuation=ContinuationInfo, window_complete=WindowSummary, done=QualityReport, error=CRPError."
15
+ }
16
+ },
17
+ "discriminator": { "propertyName": "event_type" }
18
+ }