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,560 @@
1
+ from __future__ import annotations
2
+ import hashlib
3
+ import json
4
+ import logging
5
+ import math
6
+ import os
7
+ import tempfile
8
+ import time
9
+ from contextlib import contextmanager
10
+ from typing import Any, Dict, Iterator, List, Tuple
11
+ from datetime import datetime, timezone
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # Intelligence Layer tunables
16
+ MIN_QUERY_LEN = 2
17
+ MAX_QUERY_LEN = 2000
18
+ LOW_QUALITY_AVG_SCORE = 0.60
19
+ SHORT_CONTENT_CHARS = 20
20
+ LOW_QUALITY_STREAK_THRESHOLD = 3
21
+ DEFAULT_MAX_BLOCKS = 500
22
+ REINDEX_BATCH_SIZE = 64
23
+
24
+
25
+ def _utc_now() -> datetime:
26
+ return datetime.now(timezone.utc)
27
+
28
+
29
+ def _date_slug(dt: datetime | None = None) -> str:
30
+ return (dt or _utc_now()).strftime("%Y%m%d")
31
+
32
+
33
+ def _content_hash(content: str) -> str:
34
+ return hashlib.sha256(content.strip().encode("utf-8")).hexdigest()[:8]
35
+
36
+
37
+ @contextmanager
38
+ def _exclusive_index_lock(index_path: str, *, timeout_seconds: float = 30.0) -> Iterator[None]:
39
+ lock_path = f"{index_path}.lock"
40
+ os.makedirs(os.path.dirname(index_path) or ".", exist_ok=True)
41
+ deadline = time.monotonic() + timeout_seconds
42
+ with open(lock_path, "a+b") as lock_file:
43
+ if os.name == "nt":
44
+ import msvcrt
45
+ while True:
46
+ try:
47
+ lock_file.seek(0)
48
+ msvcrt.locking(lock_file.fileno(), msvcrt.LK_NBLCK, 1)
49
+ break
50
+ except OSError:
51
+ if time.monotonic() >= deadline:
52
+ raise TimeoutError(f"Timed out waiting for intuition index lock: {lock_path}")
53
+ time.sleep(0.05)
54
+ try:
55
+ yield
56
+ finally:
57
+ lock_file.seek(0)
58
+ msvcrt.locking(lock_file.fileno(), msvcrt.LK_UNLCK, 1)
59
+ else:
60
+ import fcntl
61
+ while True:
62
+ try:
63
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
64
+ break
65
+ except BlockingIOError:
66
+ if time.monotonic() >= deadline:
67
+ raise TimeoutError(f"Timed out waiting for intuition index lock: {lock_path}")
68
+ time.sleep(0.05)
69
+ try:
70
+ yield
71
+ finally:
72
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
73
+
74
+
75
+ def _cosine_similarity(v1: list[float], v2: list[float]) -> float:
76
+ if not v1 or not v2 or len(v1) != len(v2):
77
+ return 0.0
78
+ dot = sum(a * b for a, b in zip(v1, v2))
79
+ norm1 = math.sqrt(sum(a * a for a in v1))
80
+ norm2 = math.sqrt(sum(b * b for b in v2))
81
+ if norm1 == 0.0 or norm2 == 0.0:
82
+ return 0.0
83
+ return dot / (norm1 * norm2)
84
+
85
+
86
+ class IntuitionEngine:
87
+ def __init__(self, index_path: str | None = None, config: dict[str, Any] | None = None):
88
+ if index_path is None:
89
+ try:
90
+ from .config import CONFIG_DIR
91
+ index_path = str(CONFIG_DIR / "intuition_index.json")
92
+ except Exception:
93
+ index_path = os.path.expanduser("~/.algo_cli/intuition_index.json")
94
+ self.index_path = index_path
95
+ self.config = {"recall_enabled": False, "max_blocks": DEFAULT_MAX_BLOCKS}
96
+ if config:
97
+ self.config.update(config)
98
+ self.blocks: Dict[str, Dict] = {}
99
+ self._load_index()
100
+ self._low_quality_count = 0
101
+ self._last_verdict: Dict[str, Any] = {}
102
+
103
+ def _load_index(self):
104
+ if not os.path.exists(self.index_path):
105
+ self.blocks = {}
106
+ return
107
+ try:
108
+ with open(self.index_path, "r", encoding="utf-8") as f:
109
+ data = json.load(f)
110
+ blocks = data.get("blocks", {}) if isinstance(data, dict) else {}
111
+ self.blocks = blocks if isinstance(blocks, dict) else {}
112
+ except Exception:
113
+ pass
114
+
115
+ def _save_index(self):
116
+ os.makedirs(os.path.dirname(self.index_path), exist_ok=True)
117
+ payload = json.dumps({
118
+ "blocks": self.blocks,
119
+ "updated": _utc_now().isoformat()
120
+ }, indent=2)
121
+ fd, tmp_path = tempfile.mkstemp(
122
+ prefix=f".{os.path.basename(self.index_path)}.",
123
+ suffix=".tmp",
124
+ dir=os.path.dirname(self.index_path),
125
+ )
126
+ try:
127
+ with os.fdopen(fd, "w", encoding="utf-8") as f:
128
+ f.write(payload)
129
+ f.flush()
130
+ os.fsync(f.fileno())
131
+ for attempt in range(8):
132
+ try:
133
+ os.replace(tmp_path, self.index_path)
134
+ break
135
+ except PermissionError:
136
+ if attempt >= 7:
137
+ raise
138
+ time.sleep(0.05 * (attempt + 1))
139
+ except Exception:
140
+ try:
141
+ os.unlink(tmp_path)
142
+ except OSError:
143
+ pass
144
+ raise
145
+
146
+ def _duplicate_block_id(self, block_type: str, content_hash: str) -> str | None:
147
+ for block_id, block in self.blocks.items():
148
+ metadata = block.get("metadata")
149
+ if (
150
+ block.get("type") == block_type
151
+ and isinstance(metadata, dict)
152
+ and metadata.get("content_hash") == content_hash
153
+ ):
154
+ return block_id
155
+ return None
156
+
157
+ def index_blocks(self, blocks: List[Dict[str, Any]]) -> list[str]:
158
+ """Index multiple blocks under one lock and one atomic write."""
159
+ if not blocks:
160
+ return []
161
+ now = _utc_now()
162
+ records: list[dict[str, Any]] = []
163
+ for block in blocks:
164
+ block_id = block.get("id") or f"{block.get('type', 'general')}:{_date_slug(now)}:{_content_hash(str(block.get('content', '')))}"
165
+ records.append({
166
+ "id": block_id,
167
+ "content": block.get("content", ""),
168
+ "type": block.get("type", "general"),
169
+ "timestamp": block.get("timestamp") or now.isoformat(),
170
+ "metadata": block.get("metadata", {}),
171
+ "embedding": block.get("embedding"),
172
+ })
173
+
174
+ indexed_ids: list[str] = []
175
+ with _exclusive_index_lock(self.index_path):
176
+ self._load_index()
177
+ for record in records:
178
+ metadata = record.get("metadata")
179
+ content_hash = metadata.get("content_hash") if isinstance(metadata, dict) else None
180
+ duplicate_id = (
181
+ self._duplicate_block_id(str(record.get("type", "general")), str(content_hash))
182
+ if content_hash
183
+ else None
184
+ )
185
+ if duplicate_id is not None:
186
+ indexed_ids.append(duplicate_id)
187
+ continue
188
+ block_id = str(record["id"])
189
+ self.blocks[block_id] = record
190
+ indexed_ids.append(block_id)
191
+ self._prune()
192
+ self._save_index()
193
+ return indexed_ids
194
+
195
+ def index_block(self, block: Dict[str, Any]):
196
+ return self.index_blocks([block])[0]
197
+
198
+ def _prune(self) -> None:
199
+ max_blocks = max(1, int(self.config.get("max_blocks", DEFAULT_MAX_BLOCKS)))
200
+ if len(self.blocks) <= max_blocks:
201
+ return
202
+ ordered = sorted(
203
+ self.blocks.items(),
204
+ key=lambda item: str(item[1].get("timestamp", "")),
205
+ reverse=True,
206
+ )
207
+ self.blocks = dict(ordered[:max_blocks])
208
+
209
+ def capture_block(
210
+ self,
211
+ block_type: str,
212
+ content: str,
213
+ *,
214
+ source: str,
215
+ embed_fn: Any | None = None,
216
+ embedding_model: str | None = None,
217
+ metadata: dict[str, Any] | None = None,
218
+ ) -> str:
219
+ """Capture a reusable block, embedding at capture time when possible."""
220
+ clean_type = (block_type or "general").strip().lower() or "general"
221
+ clean_content = (content or "").strip()
222
+ if not clean_content:
223
+ raise ValueError("Intuition block content was empty.")
224
+
225
+ existing_hash = _content_hash(clean_content)
226
+ duplicate_id = self._duplicate_block_id(clean_type, existing_hash)
227
+ if duplicate_id is not None:
228
+ # Re-check under the cross-process lock. Another process may have
229
+ # forgotten the block since this engine last loaded its index.
230
+ with _exclusive_index_lock(self.index_path):
231
+ self._load_index()
232
+ duplicate_id = self._duplicate_block_id(clean_type, existing_hash)
233
+ if duplicate_id is not None:
234
+ return duplicate_id
235
+
236
+ meta = dict(metadata or {})
237
+ meta.update({"source": source, "content_hash": existing_hash})
238
+ embedding: list[float] | None = None
239
+ if embed_fn is not None:
240
+ try:
241
+ vectors = embed_fn([clean_content])
242
+ if vectors:
243
+ embedding = [float(value) for value in vectors[0]]
244
+ meta["embedding_status"] = "ready"
245
+ if embedding_model:
246
+ meta["embedding_model"] = embedding_model
247
+ except Exception as exc:
248
+ logger.debug("Intuition capture embedding failed: %s", exc)
249
+ meta["embedding_status"] = "pending"
250
+ if embedding_model:
251
+ meta["embedding_model"] = embedding_model
252
+ else:
253
+ meta["embedding_status"] = "pending"
254
+ if embedding_model:
255
+ meta["embedding_model"] = embedding_model
256
+
257
+ now = _utc_now()
258
+ block_id = f"{clean_type}:{_date_slug(now)}:{existing_hash}"
259
+ return self.index_block(
260
+ {
261
+ "id": block_id,
262
+ "type": clean_type,
263
+ "content": clean_content,
264
+ "timestamp": now.isoformat(),
265
+ "metadata": meta,
266
+ "embedding": embedding,
267
+ }
268
+ )
269
+
270
+ def status(self) -> dict[str, Any]:
271
+ embedded = 0
272
+ pending = 0
273
+ by_type: dict[str, int] = {}
274
+ for block in self.blocks.values():
275
+ by_type[str(block.get("type", "general"))] = by_type.get(str(block.get("type", "general")), 0) + 1
276
+ if block.get("embedding"):
277
+ embedded += 1
278
+ else:
279
+ pending += 1
280
+ return {
281
+ "index_path": self.index_path,
282
+ "block_count": len(self.blocks),
283
+ "embedded": embedded,
284
+ "pending": pending,
285
+ "by_type": by_type,
286
+ "recall_enabled": bool(self.config.get("recall_enabled", False)),
287
+ "max_blocks": int(self.config.get("max_blocks", DEFAULT_MAX_BLOCKS)),
288
+ }
289
+
290
+ def list_blocks(self) -> list[dict[str, Any]]:
291
+ return sorted(
292
+ (dict(block) for block in self.blocks.values()),
293
+ key=lambda block: str(block.get("timestamp", "")),
294
+ reverse=True,
295
+ )
296
+
297
+ def forget_block(self, block_id: str) -> dict[str, Any] | None:
298
+ with _exclusive_index_lock(self.index_path):
299
+ self._load_index()
300
+ removed = self.blocks.pop(block_id, None)
301
+ if removed is not None:
302
+ self._save_index()
303
+ return removed
304
+
305
+ def reindex(
306
+ self,
307
+ embed_fn: Any,
308
+ embedding_model: str | None = None,
309
+ *,
310
+ batch_size: int = REINDEX_BATCH_SIZE,
311
+ ) -> dict[str, Any]:
312
+ if embed_fn is None:
313
+ return {"ok": False, "reason": "no_embed_fn", "updated": 0, "total": len(self.blocks)}
314
+ with _exclusive_index_lock(self.index_path):
315
+ self._load_index()
316
+ snapshot = {block_id: dict(block) for block_id, block in self.blocks.items()}
317
+ updated_blocks: dict[str, dict[str, Any]] = {}
318
+ updated = 0
319
+ failed = 0
320
+ pending: list[tuple[str, dict[str, Any], dict[str, Any], str]] = []
321
+ for block_id, block in snapshot.items():
322
+ content = str(block.get("content", "")).strip()
323
+ metadata = block.get("metadata")
324
+ if not isinstance(metadata, dict):
325
+ metadata = {}
326
+ else:
327
+ metadata = dict(metadata)
328
+ block["metadata"] = metadata
329
+ if not content:
330
+ failed += 1
331
+ metadata["embedding_status"] = "failed"
332
+ updated_blocks[block_id] = block
333
+ continue
334
+ pending.append((block_id, block, metadata, content))
335
+
336
+ batch_size = max(1, int(batch_size))
337
+ for start in range(0, len(pending), batch_size):
338
+ batch = pending[start:start + batch_size]
339
+ try:
340
+ vectors = embed_fn([content for _block_id, _block, _metadata, content in batch])
341
+ if len(vectors) != len(batch):
342
+ raise ValueError("embedding response count did not match batch")
343
+ except Exception as exc:
344
+ logger.debug("Intuition reindex batch failed: %s", exc)
345
+ for block_id, block, metadata, _content in batch:
346
+ metadata["embedding_status"] = "failed"
347
+ failed += 1
348
+ updated_blocks[block_id] = block
349
+ continue
350
+ for (block_id, block, metadata, _content), vector in zip(batch, vectors):
351
+ try:
352
+ block["embedding"] = [float(value) for value in vector]
353
+ except (TypeError, ValueError) as exc:
354
+ logger.debug("Intuition reindex vector conversion failed for %s: %s", block_id, exc)
355
+ metadata["embedding_status"] = "failed"
356
+ failed += 1
357
+ else:
358
+ metadata["embedding_status"] = "ready"
359
+ if embedding_model:
360
+ metadata["embedding_model"] = embedding_model
361
+ updated += 1
362
+ updated_blocks[block_id] = block
363
+ with _exclusive_index_lock(self.index_path):
364
+ self._load_index()
365
+ applied = 0
366
+ for block_id, block in updated_blocks.items():
367
+ current = self.blocks.get(block_id)
368
+ if current is None:
369
+ continue
370
+ current["embedding"] = block.get("embedding")
371
+ current_meta = current.get("metadata")
372
+ if not isinstance(current_meta, dict):
373
+ current_meta = {}
374
+ current["metadata"] = current_meta
375
+ current_meta.update(block.get("metadata") or {})
376
+ applied += 1
377
+ self._save_index()
378
+ total = len(self.blocks)
379
+ return {
380
+ "ok": failed == 0,
381
+ "updated": updated,
382
+ "failed": failed,
383
+ "applied": applied,
384
+ "total": total,
385
+ "embedding_model": embedding_model,
386
+ }
387
+
388
+ def sync_from_squeezer(self, squeeze_engine: Any) -> int:
389
+ if not squeeze_engine:
390
+ return 0
391
+ try:
392
+ get_blocks = getattr(squeeze_engine, "get_new_blocks", None)
393
+ if not get_blocks:
394
+ return 0
395
+ new_blocks = get_blocks() or []
396
+ return len(self.index_blocks(list(new_blocks)))
397
+ except Exception:
398
+ return 0
399
+
400
+ # ---------- Intelligence Layer ----------
401
+
402
+ def _check_guardrails(self, query: str) -> Tuple[bool, str]:
403
+ """Pre-recall validation. Returns (ok, reason)."""
404
+ if not isinstance(query, str):
405
+ return False, "query_not_string"
406
+ stripped = query.strip()
407
+ if not stripped:
408
+ return False, "query_empty"
409
+ if len(stripped) < MIN_QUERY_LEN:
410
+ return False, "query_too_short"
411
+ if len(stripped) > MAX_QUERY_LEN:
412
+ return False, "query_too_long"
413
+ if not self.blocks:
414
+ return False, "no_blocks_indexed"
415
+ return True, "ok"
416
+
417
+ def _ground_block(self, block_id: str) -> Dict[str, Any] | None:
418
+ """Confirm a block still exists and has minimum valid metadata."""
419
+ block = self.blocks.get(block_id)
420
+ if not block:
421
+ return None
422
+ content = block.get("content", "")
423
+ if not isinstance(content, str) or not content.strip():
424
+ return None
425
+ if not block.get("timestamp"):
426
+ return None
427
+ return block
428
+
429
+ def _verify_results(self, results: List[Dict]) -> Dict[str, Any]:
430
+ """Score recall quality and detect failure modes."""
431
+ if not results:
432
+ return {"ok": False, "reason": "no_results", "avg_score": 0.0}
433
+
434
+ avg_score = sum(r["score"] for r in results) / len(results)
435
+
436
+ seen: set[str] = set()
437
+ duplicates = 0
438
+ short = 0
439
+ for r in results:
440
+ content = r.get("content", "")
441
+ digest = hashlib.sha1(content.strip().lower().encode("utf-8")).hexdigest()
442
+ if digest in seen:
443
+ duplicates += 1
444
+ seen.add(digest)
445
+ if len(content.strip()) < SHORT_CONTENT_CHARS:
446
+ short += 1
447
+
448
+ low_quality = (
449
+ avg_score < LOW_QUALITY_AVG_SCORE
450
+ or duplicates > 0
451
+ or short == len(results)
452
+ )
453
+ return {
454
+ "ok": not low_quality,
455
+ "avg_score": round(avg_score, 3),
456
+ "duplicates": duplicates,
457
+ "short_content_hits": short,
458
+ "count": len(results),
459
+ }
460
+
461
+ # ---------- Recall ----------
462
+
463
+ def recall(
464
+ self,
465
+ query: str,
466
+ top_k: int = 3,
467
+ min_score: float = 0.65,
468
+ *,
469
+ enabled: bool | None = None,
470
+ embed_fn: Any | None = None,
471
+ ) -> List[Dict]:
472
+ recall_enabled = self.config.get("recall_enabled", False) if enabled is None else enabled
473
+ if not recall_enabled:
474
+ logger.debug("Intuition recall disabled.")
475
+ self._last_verdict = {"ok": False, "reason": "recall_disabled"}
476
+ return []
477
+ if embed_fn is None:
478
+ self._last_verdict = {"ok": False, "reason": "no_embed_fn"}
479
+ return []
480
+ ok, reason = self._check_guardrails(query)
481
+ if not ok:
482
+ self._last_verdict = {"ok": False, "reason": reason}
483
+ return []
484
+
485
+ try:
486
+ query_vectors = embed_fn([query])
487
+ except Exception as exc:
488
+ logger.debug("Intuition query embedding failed: %s", exc)
489
+ self._last_verdict = {"ok": False, "reason": "query_embedding_failed"}
490
+ return []
491
+ if not query_vectors:
492
+ self._last_verdict = {"ok": False, "reason": "query_embedding_missing"}
493
+ return []
494
+ query_vec = [float(value) for value in query_vectors[0]]
495
+
496
+ results: List[Dict] = []
497
+ for block_id, _ in self.blocks.items():
498
+ block = self._ground_block(block_id)
499
+ if not block:
500
+ continue
501
+ embedding = block.get("embedding")
502
+ if not isinstance(embedding, list):
503
+ continue
504
+
505
+ score = round(_cosine_similarity(query_vec, [float(value) for value in embedding]), 3)
506
+ if score < min_score:
507
+ continue
508
+
509
+ results.append({
510
+ "id": block_id,
511
+ "content": block.get("content", ""),
512
+ "type": block.get("type", "general"),
513
+ "score": score,
514
+ "timestamp": block.get("timestamp"),
515
+ "metadata": block.get("metadata", {}),
516
+ })
517
+
518
+ sorted_results = sorted(results, key=lambda x: x["score"], reverse=True)[:top_k]
519
+
520
+ verdict = self._verify_results(sorted_results)
521
+ self._last_verdict = verdict
522
+ if not verdict.get("ok"):
523
+ self._low_quality_count += 1
524
+ else:
525
+ self._low_quality_count = 0
526
+
527
+ return sorted_results
528
+
529
+ def format_for_injection(self, blocks: List[Dict]) -> str:
530
+ lines = ["## Relevant Context (from memory)"]
531
+ for block in blocks:
532
+ block_type = str(block.get("type", "note")).upper()
533
+ block_id = block.get("id", "?")
534
+ content = str(block.get("content", ""))
535
+ score = float(block.get("score", 0.0) or 0.0)
536
+ snippet = content[:300].replace("\n", " ").strip()
537
+ if len(content) > 300:
538
+ snippet += "..."
539
+ if snippet:
540
+ lines.append(f"- [{block_type}] `{block_id}` ({score:.2f}): {snippet}")
541
+ return "\n".join(lines) if len(lines) > 1 else ""
542
+
543
+ def run(self, query: str, *, enabled: bool | None = None, embed_fn: Any | None = None) -> str | None:
544
+ """Public adapter called by the chat loop.
545
+ """
546
+ blocks = self.recall(query, enabled=enabled, embed_fn=embed_fn)
547
+ if not blocks:
548
+ return None
549
+ injection = self.format_for_injection(blocks)
550
+ if not injection:
551
+ return None
552
+ return f"{query}\n\n{injection}"
553
+
554
+ def should_introspect(self) -> bool:
555
+ """Returns True if recall quality has been consistently low."""
556
+ return self._low_quality_count >= LOW_QUALITY_STREAK_THRESHOLD
557
+
558
+ def last_verdict(self) -> Dict[str, Any]:
559
+ """Return verification details from the most recent recall."""
560
+ return dict(self._last_verdict)
@@ -0,0 +1,82 @@
1
+ from typing import Any
2
+ import logging
3
+ import time
4
+
5
+ logger = logging.getLogger("intuition_injector")
6
+
7
+ _recent_recalls: dict[str, float] = {}
8
+ _CACHE_TTL = 300
9
+
10
+
11
+ def _format_recall_block(block: dict) -> str:
12
+ """Produce a clean, evidence-backed recall block."""
13
+ block_id = block.get("id", "unknown")
14
+ score = block.get("score", 0)
15
+ content = block.get("content", "").strip()
16
+ block_type = block.get("type", "general")
17
+ timestamp = block.get("timestamp", "")
18
+
19
+ header = f"**[{block_type.upper()}]** `{block_id}` - Score: {score:.2f}"
20
+ if timestamp:
21
+ header += f" - {timestamp[:10]}"
22
+
23
+ # Keep context small and focused.
24
+ body = content[:450].replace("\n", " ").strip()
25
+ if len(content) > 450:
26
+ body += "..."
27
+
28
+ return f"{header}\n{body}"
29
+
30
+
31
+ def inject_intuition(
32
+ system_prompt: str,
33
+ user_input: str,
34
+ intuition_engine: Any,
35
+ top_k: int = 3,
36
+ min_score: float = 0.65,
37
+ max_tokens: int = 1200,
38
+ ) -> str:
39
+ """Inject relevant intuition recalls with structured, polished format."""
40
+ if not intuition_engine:
41
+ return system_prompt
42
+
43
+ try:
44
+ recalled = intuition_engine.recall(user_input, top_k=top_k, min_score=min_score)
45
+ if not recalled:
46
+ return system_prompt
47
+
48
+ # Filter recent recalls.
49
+ now = time.time()
50
+ filtered = []
51
+ for block in recalled:
52
+ block_id = block.get("id", "")
53
+ if block_id and _recent_recalls.get(block_id, 0) > now - _CACHE_TTL:
54
+ continue
55
+ filtered.append(block)
56
+ if block_id:
57
+ _recent_recalls[block_id] = now
58
+
59
+ if not filtered:
60
+ return system_prompt
61
+
62
+ # Build structured output within token budget.
63
+ blocks_text = ""
64
+ current_tokens = 0
65
+ for block in filtered:
66
+ block_text = _format_recall_block(block) + "\n\n"
67
+ block_tokens = len(block_text) // 4
68
+
69
+ if current_tokens + block_tokens > max_tokens:
70
+ break
71
+
72
+ blocks_text += block_text
73
+ current_tokens += block_tokens
74
+
75
+ if not blocks_text.strip():
76
+ return system_prompt
77
+
78
+ return f"{system_prompt}\n\n### Relevant Intuition\n{blocks_text.strip()}"
79
+
80
+ except Exception as exc:
81
+ logger.warning("Intuition recall failed: %s", exc)
82
+ return system_prompt
@@ -0,0 +1,5 @@
1
+ """Promoted Algo CLI kernel registry."""
2
+
3
+ from .manifest import KernelSpec, get_kernel, kernel_names, list_kernels
4
+
5
+ __all__ = ["KernelSpec", "get_kernel", "kernel_names", "list_kernels"]