algo-cli-runtime 0.14.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 (237) hide show
  1. algo_cli/__init__.py +3 -0
  2. algo_cli/__main__.py +7 -0
  3. algo_cli/_internal/__init__.py +12 -0
  4. algo_cli/_internal/policy_chain.py +259 -0
  5. algo_cli/action_registry.py +1047 -0
  6. algo_cli/agent_blocks.py +550 -0
  7. algo_cli/agent_pipeline.py +1457 -0
  8. algo_cli/agent_threads.py +308 -0
  9. algo_cli/animations.py +316 -0
  10. algo_cli/cache_admission.py +209 -0
  11. algo_cli/capability_mask.py +66 -0
  12. algo_cli/chat_protocol.py +116 -0
  13. algo_cli/chatgpt_auth.py +510 -0
  14. algo_cli/chatgpt_client.py +657 -0
  15. algo_cli/code_rag.py +479 -0
  16. algo_cli/config.py +651 -0
  17. algo_cli/context_budget.py +679 -0
  18. algo_cli/credential_helpers.py +315 -0
  19. algo_cli/deliberation.py +29 -0
  20. algo_cli/display.py +1470 -0
  21. algo_cli/evals/__init__.py +21 -0
  22. algo_cli/evals/algorithm_effectiveness.py +560 -0
  23. algo_cli/evals/competitive_harness_rating.py +702 -0
  24. algo_cli/evals/cot_quality.py +220 -0
  25. algo_cli/evals/harness_retrieval_benchmark.py +401 -0
  26. algo_cli/evals/performance_regression.py +136 -0
  27. algo_cli/evals/scorecard_grading.py +308 -0
  28. algo_cli/evals/session_distribution.py +84 -0
  29. algo_cli/execution_guardrails.py +806 -0
  30. algo_cli/extensions_manifest.py +84 -0
  31. algo_cli/git_evidence.py +227 -0
  32. algo_cli/google_workspace.py +407 -0
  33. algo_cli/google_workspace_auth.py +523 -0
  34. algo_cli/harness.py +2587 -0
  35. algo_cli/identity.py +557 -0
  36. algo_cli/index_compute_lab.py +228 -0
  37. algo_cli/inference_harness.py +70 -0
  38. algo_cli/intelligence/__init__.py +1103 -0
  39. algo_cli/intelligence/acrobat_config.py +307 -0
  40. algo_cli/intelligence/acrobat_manifests.py +338 -0
  41. algo_cli/intelligence/acrobat_models.py +195 -0
  42. algo_cli/intelligence/acrobat_pipeline.py +295 -0
  43. algo_cli/intelligence/acrobat_runtime.py +302 -0
  44. algo_cli/intelligence/acrobat_security.py +261 -0
  45. algo_cli/intelligence/acrobat_workflows.py +226 -0
  46. algo_cli/intelligence/actionability.py +165 -0
  47. algo_cli/intelligence/adversarial_audit.py +136 -0
  48. algo_cli/intelligence/agent_arena.py +92 -0
  49. algo_cli/intelligence/agent_benchmark.py +236 -0
  50. algo_cli/intelligence/agent_runtime.py +171 -0
  51. algo_cli/intelligence/agents_as_tools.py +70 -0
  52. algo_cli/intelligence/artifact_binding.py +80 -0
  53. algo_cli/intelligence/autonomous_engineer.py +1976 -0
  54. algo_cli/intelligence/backpressure.py +99 -0
  55. algo_cli/intelligence/bloom_filter.py +186 -0
  56. algo_cli/intelligence/bonferroni.py +66 -0
  57. algo_cli/intelligence/boundary_compaction.py +98 -0
  58. algo_cli/intelligence/catalog_verifier.py +172 -0
  59. algo_cli/intelligence/cavecrew.py +118 -0
  60. algo_cli/intelligence/changelog.py +176 -0
  61. algo_cli/intelligence/checkpoint_resume.py +92 -0
  62. algo_cli/intelligence/circuit_breaker.py +88 -0
  63. algo_cli/intelligence/clarification_gate.py +101 -0
  64. algo_cli/intelligence/code_graph.py +180 -0
  65. algo_cli/intelligence/coderank.py +97 -0
  66. algo_cli/intelligence/consistent_hash.py +150 -0
  67. algo_cli/intelligence/consortium_synthesis.py +139 -0
  68. algo_cli/intelligence/construction/__init__.py +241 -0
  69. algo_cli/intelligence/construction/common.py +273 -0
  70. algo_cli/intelligence/construction/documents.py +496 -0
  71. algo_cli/intelligence/construction/labor_units.py +1395 -0
  72. algo_cli/intelligence/construction/payments.py +470 -0
  73. algo_cli/intelligence/construction/risk.py +784 -0
  74. algo_cli/intelligence/content_extractor.py +132 -0
  75. algo_cli/intelligence/context_adaptive.py +102 -0
  76. algo_cli/intelligence/context_ops.py +95 -0
  77. algo_cli/intelligence/count_min.py +145 -0
  78. algo_cli/intelligence/cow_state.py +103 -0
  79. algo_cli/intelligence/critic_loop.py +119 -0
  80. algo_cli/intelligence/cross_source.py +113 -0
  81. algo_cli/intelligence/daemon_mode.py +99 -0
  82. algo_cli/intelligence/dag_orchestration.py +151 -0
  83. algo_cli/intelligence/deep_research.py +155 -0
  84. algo_cli/intelligence/degenerate_detector.py +78 -0
  85. algo_cli/intelligence/delta_report.py +92 -0
  86. algo_cli/intelligence/discovery_event_log.py +92 -0
  87. algo_cli/intelligence/document_ingest.py +298 -0
  88. algo_cli/intelligence/dual_layer_validate.py +151 -0
  89. algo_cli/intelligence/echo_fidelity.py +73 -0
  90. algo_cli/intelligence/ema_tuning.py +104 -0
  91. algo_cli/intelligence/event_log.py +92 -0
  92. algo_cli/intelligence/evidence_graph.py +114 -0
  93. algo_cli/intelligence/extension_host.py +162 -0
  94. algo_cli/intelligence/extension_manifest.py +115 -0
  95. algo_cli/intelligence/falsification_suite.py +178 -0
  96. algo_cli/intelligence/finance/__init__.py +169 -0
  97. algo_cli/intelligence/finance/anomalies.py +135 -0
  98. algo_cli/intelligence/finance/ap_ar.py +351 -0
  99. algo_cli/intelligence/finance/cash.py +162 -0
  100. algo_cli/intelligence/finance/close.py +332 -0
  101. algo_cli/intelligence/finance/common.py +244 -0
  102. algo_cli/intelligence/finance/construction.py +135 -0
  103. algo_cli/intelligence/finance/controls.py +172 -0
  104. algo_cli/intelligence/finance/evidence.py +119 -0
  105. algo_cli/intelligence/finance/exceptions.py +157 -0
  106. algo_cli/intelligence/finance/reconciliations.py +254 -0
  107. algo_cli/intelligence/finance/revenue.py +109 -0
  108. algo_cli/intelligence/finance/tax.py +74 -0
  109. algo_cli/intelligence/finance/workpapers.py +111 -0
  110. algo_cli/intelligence/finding_record.py +120 -0
  111. algo_cli/intelligence/flow_dag.py +267 -0
  112. algo_cli/intelligence/gatherer.py +223 -0
  113. algo_cli/intelligence/golden_master.py +98 -0
  114. algo_cli/intelligence/graph_rag.py +195 -0
  115. algo_cli/intelligence/group_chat.py +143 -0
  116. algo_cli/intelligence/hash_dedup.py +145 -0
  117. algo_cli/intelligence/hyperloglog.py +128 -0
  118. algo_cli/intelligence/incremental_index.py +316 -0
  119. algo_cli/intelligence/index_store.py +16 -0
  120. algo_cli/intelligence/iteration_plan.py +133 -0
  121. algo_cli/intelligence/kernel_plugins.py +167 -0
  122. algo_cli/intelligence/lesson_catalog.py +135 -0
  123. algo_cli/intelligence/llm_fallback.py +169 -0
  124. algo_cli/intelligence/log2_histogram.py +267 -0
  125. algo_cli/intelligence/lsp_integration.py +147 -0
  126. algo_cli/intelligence/memory_evolution.py +117 -0
  127. algo_cli/intelligence/minhash_lsh.py +182 -0
  128. algo_cli/intelligence/multi_model_score.py +174 -0
  129. algo_cli/intelligence/multi_tier_grade.py +211 -0
  130. algo_cli/intelligence/negative_controls.py +113 -0
  131. algo_cli/intelligence/numeric_clamp.py +63 -0
  132. algo_cli/intelligence/occ_editor.py +66 -0
  133. algo_cli/intelligence/output_normalize.py +112 -0
  134. algo_cli/intelligence/parallel_delegation.py +98 -0
  135. algo_cli/intelligence/parallel_fanout.py +104 -0
  136. algo_cli/intelligence/permission_modes.py +105 -0
  137. algo_cli/intelligence/pre_push_gate.py +68 -0
  138. algo_cli/intelligence/prefetch.py +171 -0
  139. algo_cli/intelligence/process_framework.py +217 -0
  140. algo_cli/intelligence/project_graph.py +387 -0
  141. algo_cli/intelligence/query_expansion.py +146 -0
  142. algo_cli/intelligence/ralph_loop.py +117 -0
  143. algo_cli/intelligence/rate_limiter.py +153 -0
  144. algo_cli/intelligence/refactor_transaction.py +94 -0
  145. algo_cli/intelligence/research_workspace.py +108 -0
  146. algo_cli/intelligence/retraction_ledger.py +72 -0
  147. algo_cli/intelligence/saga_pattern.py +88 -0
  148. algo_cli/intelligence/session_fork.py +100 -0
  149. algo_cli/intelligence/shadow_editor.py +67 -0
  150. algo_cli/intelligence/shell_session.py +213 -0
  151. algo_cli/intelligence/source_registry.py +143 -0
  152. algo_cli/intelligence/spawn_scales.py +99 -0
  153. algo_cli/intelligence/stat_stability.py +104 -0
  154. algo_cli/intelligence/structural_validator.py +148 -0
  155. algo_cli/intelligence/subagent_spawner.py +111 -0
  156. algo_cli/intelligence/symmetric_verify.py +70 -0
  157. algo_cli/intelligence/task_classifier.py +129 -0
  158. algo_cli/intelligence/team_execution.py +122 -0
  159. algo_cli/intelligence/tiered_access.py +121 -0
  160. algo_cli/intelligence/utility_registry.py +159 -0
  161. algo_cli/intuition_engine.py +560 -0
  162. algo_cli/intuition_injector.py +82 -0
  163. algo_cli/kernels/__init__.py +5 -0
  164. algo_cli/kernels/manifest.py +763 -0
  165. algo_cli/main.py +3903 -0
  166. algo_cli/memory_candidates.py +541 -0
  167. algo_cli/memory_echo_veil.py +394 -0
  168. algo_cli/memory_runtime.py +112 -0
  169. algo_cli/model_info.py +548 -0
  170. algo_cli/model_profile.py +160 -0
  171. algo_cli/model_routing.py +74 -0
  172. algo_cli/oneshot.py +331 -0
  173. algo_cli/perf_telemetry.py +389 -0
  174. algo_cli/plugins.py +245 -0
  175. algo_cli/private_event_store.py +654 -0
  176. algo_cli/quantization/__init__.py +24 -0
  177. algo_cli/quantization/lloyd_max.py +98 -0
  178. algo_cli/quantization/turbo_quant.py +308 -0
  179. algo_cli/reasoning/__init__.py +46 -0
  180. algo_cli/reasoning/combinatorial.py +356 -0
  181. algo_cli/reasoning/graph_of_thought.py +297 -0
  182. algo_cli/reasoning/mcts.py +220 -0
  183. algo_cli/reasoning/neuro_symbolic.py +250 -0
  184. algo_cli/reasoning/react.py +246 -0
  185. algo_cli/reasoning/reflexion.py +225 -0
  186. algo_cli/reasoning/tree_of_thought.py +241 -0
  187. algo_cli/reasoning_bridge.py +150 -0
  188. algo_cli/reconciliation.py +284 -0
  189. algo_cli/reflex.py +385 -0
  190. algo_cli/resources/docs/ALGO.md +13958 -0
  191. algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
  192. algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
  193. algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
  194. algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
  195. algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
  196. algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
  197. algo_cli/resources/docs/main-split-map.md +35 -0
  198. algo_cli/resources/docs/privacy-and-context.md +48 -0
  199. algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
  200. algo_cli/resources/skills/README.md +26 -0
  201. algo_cli/resources/skills/algo-cli.md +59 -0
  202. algo_cli/resources/skills/edit-file-precision.md +49 -0
  203. algo_cli/resources/skills/harness-search-first.md +47 -0
  204. algo_cli/resources/skills/memory-recall-ritual.md +51 -0
  205. algo_cli/resources/skills/qol-algorithms.md +224 -0
  206. algo_cli/resources/skills/smart-error-recovery.md +56 -0
  207. algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
  208. algo_cli/retrieval_algorithms.py +127 -0
  209. algo_cli/runtime_qos.py +236 -0
  210. algo_cli/runtime_services.py +320 -0
  211. algo_cli/session_commands.py +95 -0
  212. algo_cli/session_mode.py +113 -0
  213. algo_cli/skills.py +430 -0
  214. algo_cli/slash_dispatch.py +1265 -0
  215. algo_cli/small_context.py +206 -0
  216. algo_cli/spawn_budget.py +89 -0
  217. algo_cli/task_ledger.py +84 -0
  218. algo_cli/task_router.py +197 -0
  219. algo_cli/tool_context.py +94 -0
  220. algo_cli/tool_contract.py +99 -0
  221. algo_cli/tool_policy.py +357 -0
  222. algo_cli/tool_runtime.py +647 -0
  223. algo_cli/tools.py +3056 -0
  224. algo_cli/url_scheme.py +174 -0
  225. algo_cli/verify.py +154 -0
  226. algo_cli/version_manifest.py +178 -0
  227. algo_cli/vision_screenshot_verify.py +76 -0
  228. algo_cli/workspace_resolver.py +68 -0
  229. algo_cli/x_account.py +209 -0
  230. algo_cli/xai_auth.py +374 -0
  231. algo_cli/xai_client.py +600 -0
  232. algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
  233. algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
  234. algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
  235. algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
  236. algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
  237. ollama_cli/__init__.py +67 -0
@@ -0,0 +1,195 @@
1
+ """B160, B172: Acrobat-derived model and hardware compatibility patterns.
2
+
3
+ - B160: Local Model Manifest Registry + Tensor Schema Gate
4
+ - B172: Hardware/Platform Compatibility Table
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Any
11
+
12
+
13
+ # ── B160: Local Model Manifest Registry + Tensor Schema Gate ──────────
14
+
15
+
16
+ @dataclass
17
+ class TensorSpec:
18
+ """A tensor input/output specification."""
19
+ name: str
20
+ shape: list[int]
21
+ data_type: str # "float", "int64", etc.
22
+ required: bool = True
23
+
24
+
25
+ @dataclass
26
+ class ModelComponent:
27
+ """A component file for a model."""
28
+ name: str
29
+ path: str
30
+ encryption_key: str = "none"
31
+
32
+
33
+ @dataclass
34
+ class ModelTarget:
35
+ """A runtime target for a model."""
36
+ runtime: str # "WinML", "onnxruntime", "ollama", etc.
37
+ components: list[ModelComponent] = field(default_factory=list)
38
+ inputs: list[TensorSpec] = field(default_factory=list)
39
+ outputs: list[TensorSpec] = field(default_factory=list)
40
+
41
+
42
+ @dataclass
43
+ class ModelManifest:
44
+ """A local model manifest (B160)."""
45
+ id: str
46
+ name: str
47
+ author: str = ""
48
+ version: int = 1
49
+ targets: dict[str, ModelTarget] = field(default_factory=dict)
50
+
51
+ def get_target(self, runtime: str) -> ModelTarget | None:
52
+ return self.targets.get(runtime)
53
+
54
+
55
+ class ModelSchemaGate:
56
+ """Validates tensor schemas before model invocation (B160)."""
57
+
58
+ @staticmethod
59
+ def validate_input(target: ModelTarget, provided_inputs: dict[str, Any]) -> list[str]:
60
+ """Validate provided inputs against the target's input specs.
61
+ Returns list of errors (empty = valid).
62
+ """
63
+ errors: list[str] = []
64
+ for spec in target.inputs:
65
+ if spec.required and spec.name not in provided_inputs:
66
+ errors.append(f"missing required input: {spec.name}")
67
+ continue
68
+ if spec.name in provided_inputs:
69
+ value = provided_inputs[spec.name]
70
+ # Check shape if value has shape attribute
71
+ if hasattr(value, "shape"):
72
+ actual_shape = list(value.shape)
73
+ if len(actual_shape) != len(spec.shape):
74
+ errors.append(
75
+ f"shape mismatch for {spec.name}: expected {spec.shape}, got {actual_shape}"
76
+ )
77
+ return errors
78
+
79
+ @staticmethod
80
+ def validate_runtime(target: ModelTarget, available_runtimes: set[str]) -> bool:
81
+ """Check if the target's runtime is available."""
82
+ return target.runtime in available_runtimes
83
+
84
+
85
+ class ModelManifestRegistry:
86
+ """Registry of local model manifests (B160)."""
87
+
88
+ def __init__(self) -> None:
89
+ self._manifests: dict[str, ModelManifest] = {}
90
+
91
+ def register(self, manifest: ModelManifest) -> None:
92
+ self._manifests[manifest.id] = manifest
93
+
94
+ def get(self, model_id: str) -> ModelManifest | None:
95
+ return self._manifests.get(model_id)
96
+
97
+ def load_check(
98
+ self, model_id: str, runtime: str, existing_files: set[str], available_runtimes: set[str] | None = None
99
+ ) -> tuple[bool, list[str]]:
100
+ """Check if a model can be loaded. Returns (can_load, errors)."""
101
+ manifest = self._manifests.get(model_id)
102
+ if not manifest:
103
+ return False, [f"unknown model: {model_id}"]
104
+ target = manifest.get_target(runtime)
105
+ if not target:
106
+ return False, [f"runtime not supported: {runtime}"]
107
+ if available_runtimes is not None and not ModelSchemaGate.validate_runtime(target, available_runtimes):
108
+ return False, [f"runtime not available: {runtime}"]
109
+ errors: list[str] = []
110
+ for comp in target.components:
111
+ if comp.path not in existing_files:
112
+ errors.append(f"missing component file: {comp.path}")
113
+ if errors:
114
+ return False, errors
115
+ return True, []
116
+
117
+ def all_models(self) -> list[ModelManifest]:
118
+ return list(self._manifests.values())
119
+
120
+
121
+ # ── B172: Hardware/Platform Compatibility Table ────────────────────────
122
+
123
+
124
+ @dataclass
125
+ class CompatEntry:
126
+ """A single hardware compatibility entry (B172)."""
127
+ action: str # "opt_in", "opt_out", "restrict"
128
+ vendor_id: str = ""
129
+ driver: str = ""
130
+ device_name: str = ""
131
+ flags: str = ""
132
+ feature: str = "gpu" # which feature this entry controls
133
+
134
+
135
+ @dataclass
136
+ class CompatDecision:
137
+ """Result of a compatibility check."""
138
+ enabled: bool
139
+ reason: str = ""
140
+ entry_id: str = ""
141
+
142
+
143
+ class HardwareCompatTable:
144
+ """Hardware/platform compatibility table (B172)."""
145
+
146
+ def __init__(self) -> None:
147
+ self._entries: list[CompatEntry] = []
148
+
149
+ def add_entry(self, entry: CompatEntry) -> None:
150
+ self._entries.append(entry)
151
+
152
+ def check(self, vendor_id: str, driver: str = "", device_name: str = "", feature: str = "gpu") -> CompatDecision:
153
+ """Check if a feature should be enabled for the given hardware."""
154
+ # Look for exact match first
155
+ for i, entry in enumerate(self._entries):
156
+ if entry.feature != feature:
157
+ continue
158
+ if entry.vendor_id and entry.vendor_id != vendor_id:
159
+ continue
160
+ if entry.driver and driver and entry.driver != driver:
161
+ continue
162
+ if entry.device_name and entry.device_name != device_name:
163
+ continue
164
+ if entry.action == "opt_in":
165
+ return CompatDecision(enabled=True, reason=f"opt_in by entry {i}", entry_id=str(i))
166
+ if entry.action == "opt_out":
167
+ return CompatDecision(enabled=False, reason=f"opt_out by entry {i}", entry_id=str(i))
168
+ if entry.action == "restrict":
169
+ return CompatDecision(enabled=False, reason=f"restricted by entry {i}", entry_id=str(i))
170
+ # Default: conservative opt-out for unknown hardware
171
+ return CompatDecision(enabled=False, reason="unknown hardware, conservative opt-out")
172
+
173
+ def all_entries(self) -> list[CompatEntry]:
174
+ return list(self._entries)
175
+
176
+
177
+ def default_gpu_compat_table() -> HardwareCompatTable:
178
+ """Create a default GPU compatibility table from Acrobat's AGMGPUOptIn.ini."""
179
+ table = HardwareCompatTable()
180
+ # Intel G965 Express - opt out
181
+ table.add_entry(CompatEntry(
182
+ action="opt_out", vendor_id="00008086", driver="igxprd32.dll",
183
+ device_name="G965 EXPRESS", feature="gpu",
184
+ ))
185
+ # AMD Radeon - opt in
186
+ table.add_entry(CompatEntry(
187
+ action="opt_in", vendor_id="00001002", driver="atiumdag",
188
+ device_name="RADEON", feature="gpu",
189
+ ))
190
+ # NVIDIA GeForce - opt in
191
+ table.add_entry(CompatEntry(
192
+ action="opt_in", vendor_id="000010de", driver="nvd3dum",
193
+ device_name="GEFORCE", feature="gpu",
194
+ ))
195
+ return table
@@ -0,0 +1,295 @@
1
+ """B169, B174, B176: Acrobat-derived crash/format/pipeline patterns.
2
+
3
+ - B169: Structured Crash/Failure Reporter
4
+ - B174: Format Mapping/Transformation Tables
5
+ - B176: Specialized OCR/Document Pipeline Stages
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Any, Callable, Protocol
12
+ import time
13
+
14
+
15
+ # ── B169: Structured Crash/Failure Reporter ───────────────────────────
16
+
17
+
18
+ @dataclass
19
+ class CrashReport:
20
+ """A structured crash report (B169)."""
21
+ process: str
22
+ version: str = ""
23
+ stack_trace: str = ""
24
+ context_snapshot: dict[str, Any] = field(default_factory=dict)
25
+ user_comment: str = ""
26
+ silent_send: bool = False
27
+ timestamp: float = field(default_factory=time.time)
28
+ tool_call_id: str = ""
29
+ model: str = ""
30
+ context_size: int = 0
31
+
32
+
33
+ @dataclass
34
+ class CrashReportResult:
35
+ """Result of processing a crash report."""
36
+ stored_locally: bool = False
37
+ uploaded: bool = False
38
+ display_variant: str = "medium" # "small", "medium", "large"
39
+ error: str = ""
40
+
41
+
42
+ class CrashReporter:
43
+ """Structured crash/failure reporter (B169).
44
+
45
+ Tool-call crashes do not kill the agent loop. Reports are stored
46
+ locally and optionally uploaded with user consent.
47
+ """
48
+
49
+ def __init__(self) -> None:
50
+ self._reports: list[CrashReport] = []
51
+ self._upload_enabled: bool = False
52
+ self._upload_url: str = ""
53
+
54
+ def set_upload_config(self, url: str, enabled: bool = False) -> None:
55
+ self._upload_url = url
56
+ self._upload_enabled = enabled
57
+
58
+ def report(self, crash: CrashReport, consent: bool = False) -> CrashReportResult:
59
+ """Process a crash report."""
60
+ self._reports.append(crash)
61
+ result = CrashReportResult(stored_locally=True)
62
+
63
+ # Determine display variant based on context size
64
+ if crash.context_size > 100000:
65
+ result.display_variant = "large"
66
+ elif crash.context_size < 10000:
67
+ result.display_variant = "small"
68
+ else:
69
+ result.display_variant = "medium"
70
+
71
+ # Upload only with consent and if enabled
72
+ if consent and self._upload_enabled and self._upload_url:
73
+ result.uploaded = True
74
+ return result
75
+
76
+ def get_reports(self) -> list[CrashReport]:
77
+ return list(self._reports)
78
+
79
+ def report_count(self) -> int:
80
+ return len(self._reports)
81
+
82
+ def clear_reports(self) -> None:
83
+ self._reports.clear()
84
+
85
+ def latest_report(self) -> CrashReport | None:
86
+ return self._reports[-1] if self._reports else None
87
+
88
+
89
+ class AgentLoopCrashGuard:
90
+ """Wraps tool calls so crashes don't kill the agent loop (B169)."""
91
+
92
+ def __init__(self, reporter: CrashReporter | None = None) -> None:
93
+ self._reporter = reporter or CrashReporter()
94
+
95
+ def safe_call(self, fn: Callable[..., Any], *args: Any, context: dict[str, Any] | None = None, **kwargs: Any) -> Any:
96
+ """Call a function safely, catching exceptions and reporting them."""
97
+ try:
98
+ return fn(*args, **kwargs)
99
+ except Exception as e:
100
+ crash = CrashReport(
101
+ process="agent_loop",
102
+ stack_trace=str(e),
103
+ context_snapshot=context or {},
104
+ )
105
+ self._reporter.report(crash)
106
+ return None
107
+
108
+ @property
109
+ def reporter(self) -> CrashReporter:
110
+ return self._reporter
111
+
112
+
113
+ # ── B174: Format Mapping/Transformation Tables ────────────────────────
114
+
115
+
116
+ @dataclass
117
+ class MappingRule:
118
+ """A single mapping rule (B174)."""
119
+ source_element: str
120
+ target_element: str
121
+ transform: str = "passthrough" # "passthrough", "rename", "regex", "drop"
122
+ pattern: str = ""
123
+ replacement: str = ""
124
+ warnings: list[str] = field(default_factory=list)
125
+
126
+
127
+ @dataclass
128
+ class MappingResult:
129
+ """Result of applying mapping rules."""
130
+ output: dict[str, Any] = field(default_factory=dict)
131
+ unmapped_elements: list[str] = field(default_factory=list)
132
+ warnings: list[str] = field(default_factory=list)
133
+
134
+
135
+ class FormatMappingTable:
136
+ """Declarative format mapping/transformation table (B174)."""
137
+
138
+ def __init__(self, source_format: str = "pdf", target_format: str = "text") -> None:
139
+ self.source_format = source_format
140
+ self.target_format = target_format
141
+ self._rules: dict[str, MappingRule] = {}
142
+
143
+ def add_rule(self, rule: MappingRule) -> None:
144
+ self._rules[rule.source_element] = rule
145
+
146
+ def transform(self, content: dict[str, Any]) -> MappingResult:
147
+ """Transform content using mapping rules."""
148
+ result = MappingResult()
149
+ for key, value in content.items():
150
+ rule = self._rules.get(key)
151
+ if not rule:
152
+ result.unmapped_elements.append(key)
153
+ result.output[key] = value # pass-through with warning
154
+ result.warnings.append(f"unmapped element: {key}")
155
+ continue
156
+ if rule.transform == "drop":
157
+ continue
158
+ elif rule.transform == "rename":
159
+ result.output[rule.target_element] = value
160
+ elif rule.transform == "regex" and rule.pattern:
161
+ import re
162
+ if isinstance(value, str):
163
+ result.output[rule.target_element] = re.sub(
164
+ rule.pattern, rule.replacement, value
165
+ )
166
+ else:
167
+ result.output[rule.target_element] = value
168
+ else:
169
+ result.output[rule.target_element] = value
170
+ result.warnings.extend(rule.warnings)
171
+ return result
172
+
173
+ def all_rules(self) -> list[MappingRule]:
174
+ return list(self._rules.values())
175
+
176
+
177
+ # ── B176: Specialized OCR/Document Pipeline Stages ────────────────────
178
+
179
+
180
+ @dataclass
181
+ class PipelineStageResult:
182
+ """Result of a single pipeline stage."""
183
+ stage_name: str
184
+ success: bool = True
185
+ output: Any = None
186
+ skipped: bool = False
187
+ error: str = ""
188
+ duration_ms: float = 0.0
189
+
190
+
191
+ @dataclass
192
+ class PipelineResult:
193
+ """Result of running an entire pipeline."""
194
+ stages: list[PipelineStageResult] = field(default_factory=list)
195
+ completed: bool = False
196
+ output: Any = None
197
+
198
+ @property
199
+ def failed_stages(self) -> list[PipelineStageResult]:
200
+ return [s for s in self.stages if not s.success and not s.skipped]
201
+
202
+ @property
203
+ def skipped_stages(self) -> list[PipelineStageResult]:
204
+ return [s for s in self.stages if s.skipped]
205
+
206
+
207
+ class PipelineStage(Protocol):
208
+ """Protocol for pipeline stages (B176)."""
209
+ name: str
210
+ def process(self, input_data: Any, language: str = "ENU") -> PipelineStageResult: ...
211
+
212
+
213
+ class SimpleStage:
214
+ """A simple pipeline stage with a handler function."""
215
+
216
+ def __init__(self, name: str, handler: Callable[[Any, str], Any]) -> None:
217
+ self.name = name
218
+ self._handler = handler
219
+
220
+ def process(self, input_data: Any, language: str = "ENU") -> PipelineStageResult:
221
+ start = time.monotonic()
222
+ try:
223
+ output = self._handler(input_data, language)
224
+ return PipelineStageResult(
225
+ stage_name=self.name,
226
+ success=True,
227
+ output=output,
228
+ duration_ms=(time.monotonic() - start) * 1000,
229
+ )
230
+ except Exception as e:
231
+ return PipelineStageResult(
232
+ stage_name=self.name,
233
+ success=False,
234
+ error=str(e),
235
+ duration_ms=(time.monotonic() - start) * 1000,
236
+ )
237
+
238
+
239
+ class DocumentPipeline:
240
+ """Multi-stage document processing pipeline (B176).
241
+
242
+ Each stage is independently replaceable. Stage failure degrades
243
+ gracefully instead of crashing the whole pipeline.
244
+ """
245
+
246
+ def __init__(self) -> None:
247
+ self._stages: list[PipelineStage] = []
248
+ self._available_languages: set[str] = {"ENU"}
249
+
250
+ def add_stage(self, stage: PipelineStage) -> None:
251
+ self._stages.append(stage)
252
+
253
+ def register_language(self, lang: str) -> None:
254
+ self._available_languages.add(lang)
255
+
256
+ def run(self, input_data: Any, language: str = "ENU") -> PipelineResult:
257
+ """Run the pipeline. Stages that fail are skipped with warnings."""
258
+ result = PipelineResult()
259
+ current_data = input_data
260
+
261
+ for stage in self._stages:
262
+ stage_result = stage.process(current_data, language)
263
+ result.stages.append(stage_result)
264
+
265
+ if stage_result.skipped:
266
+ continue
267
+
268
+ if not stage_result.success:
269
+ # Stage failed - degrade gracefully, continue with current data
270
+ continue
271
+
272
+ current_data = stage_result.output
273
+
274
+ result.output = current_data
275
+ result.completed = True
276
+ return result
277
+
278
+ def stage_names(self) -> list[str]:
279
+ return [s.name for s in self._stages]
280
+
281
+ def available_languages(self) -> list[str]:
282
+ return sorted(self._available_languages)
283
+
284
+
285
+ def default_ocr_pipeline() -> DocumentPipeline:
286
+ """Create a default OCR pipeline from Acrobat's PaperCapture iDRS15."""
287
+ pipeline = DocumentPipeline()
288
+ pipeline.register_language("ENU")
289
+ pipeline.register_language("JPN")
290
+ pipeline.register_language("ARA")
291
+
292
+ pipeline.add_stage(SimpleStage("preprocess", lambda data, lang: data))
293
+ pipeline.add_stage(SimpleStage("ocr", lambda data, lang: f"ocr:{data}"))
294
+ pipeline.add_stage(SimpleStage("document_output", lambda data, lang: f"out:{data}"))
295
+ return pipeline