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,113 @@
1
+ """B66. Cross-Source Analysis.
2
+
3
+ Sentiment, consensus, disagreements across sources.
4
+ Source: sibyl pattern.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+ from enum import Enum, auto
10
+ import re
11
+
12
+
13
+ class Sentiment(Enum):
14
+ POSITIVE = auto()
15
+ NEUTRAL = auto()
16
+ NEGATIVE = auto()
17
+ MIXED = auto()
18
+
19
+
20
+ @dataclass
21
+ class SourcePosition:
22
+ source: str
23
+ title: str
24
+ position: str # summary of what this source says
25
+ sentiment: Sentiment = Sentiment.NEUTRAL
26
+ key_points: list[str] = field(default_factory=list)
27
+
28
+
29
+ @dataclass
30
+ class CrossSourceAnalysis:
31
+ question: str
32
+ positions: list[SourcePosition] = field(default_factory=list)
33
+ consensus: list[str] = field(default_factory=list)
34
+ disagreements: list[str] = field(default_factory=list)
35
+ overall_sentiment: Sentiment = Sentiment.NEUTRAL
36
+ source_count: int = 0
37
+
38
+
39
+ class CrossSourceAnalyzer:
40
+ """Analyze agreement/disagreement across multiple sources."""
41
+
42
+ POSITIVE_WORDS = {"good", "great", "excellent", "best", "recommend", "fast", "efficient", "easy", "powerful", "effective"}
43
+ NEGATIVE_WORDS = {"bad", "poor", "slow", "broken", "fail", "issue", "problem", "difficult", "complex", "deprecated"}
44
+
45
+ def analyze(self, question: str,
46
+ sources: list[tuple[str, str, str]]) -> CrossSourceAnalysis:
47
+ """Analyze positions across sources.
48
+
49
+ Args:
50
+ question: The question being analyzed
51
+ sources: List of (source_name, title, content)
52
+ """
53
+ result = CrossSourceAnalysis(question=question, source_count=len(sources))
54
+
55
+ for src_name, title, content in sources:
56
+ position = SourcePosition(source=src_name, title=title, position=content[:200])
57
+ position.sentiment = self._detect_sentiment(content)
58
+ position.key_points = self._extract_key_points(content)
59
+ result.positions.append(position)
60
+
61
+ result.consensus = self._find_consensus(result.positions)
62
+ result.disagreements = self._find_disagreements(result.positions)
63
+ result.overall_sentiment = self._overall_sentiment(result.positions)
64
+
65
+ return result
66
+
67
+ def _detect_sentiment(self, text: str) -> Sentiment:
68
+ words = set(text.lower().split())
69
+ pos = len(words & self.POSITIVE_WORDS)
70
+ neg = len(words & self.NEGATIVE_WORDS)
71
+ if pos > neg and pos > 0:
72
+ return Sentiment.POSITIVE
73
+ elif neg > pos and neg > 0:
74
+ return Sentiment.NEGATIVE
75
+ elif pos > 0 and neg > 0:
76
+ return Sentiment.MIXED
77
+ return Sentiment.NEUTRAL
78
+
79
+ def _extract_key_points(self, content: str) -> list[str]:
80
+ sentences = re.split(r"[.!?]+", content)
81
+ return [s.strip() for s in sentences if len(s.strip()) > 20][:5]
82
+
83
+ def _find_consensus(self, positions: list[SourcePosition]) -> list[str]:
84
+ """Find points mentioned by multiple sources."""
85
+ point_counts: dict[str, int] = {}
86
+ for pos in positions:
87
+ for point in pos.key_points:
88
+ key = point.lower()[:50]
89
+ point_counts[key] = point_counts.get(key, 0) + 1
90
+ return [p for p, c in point_counts.items() if c >= 2]
91
+
92
+ def _find_disagreements(self, positions: list[SourcePosition]) -> list[str]:
93
+ """Find where sources have different sentiments."""
94
+ disagreements: list[str] = []
95
+ sentiments = {p.source: p.sentiment for p in positions}
96
+ unique = set(sentiments.values())
97
+ if len(unique) > 1:
98
+ disagreements.append(f"Sources disagree on sentiment: {sentiments}")
99
+ return disagreements
100
+
101
+ def _overall_sentiment(self, positions: list[SourcePosition]) -> Sentiment:
102
+ if not positions:
103
+ return Sentiment.NEUTRAL
104
+ sentiments = [p.sentiment for p in positions]
105
+ pos = sum(1 for s in sentiments if s == Sentiment.POSITIVE)
106
+ neg = sum(1 for s in sentiments if s == Sentiment.NEGATIVE)
107
+ if pos > neg:
108
+ return Sentiment.POSITIVE
109
+ elif neg > pos:
110
+ return Sentiment.NEGATIVE
111
+ elif pos > 0 and neg > 0:
112
+ return Sentiment.MIXED
113
+ return Sentiment.NEUTRAL
@@ -0,0 +1,99 @@
1
+ """B59. Daemon Mode: Multi-Client Shared Agent.
2
+
3
+ HTTP+SSE server: multiple clients share one agent session.
4
+ Source: qwen-code pattern.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, Callable
11
+
12
+
13
+ @dataclass
14
+ class ClientSession:
15
+ client_id: str
16
+ connected_at: float = field(default_factory=time.time)
17
+ last_active: float = field(default_factory=time.time)
18
+ subscriptions: set[str] = field(default_factory=set)
19
+
20
+
21
+ @dataclass
22
+ class SharedMessage:
23
+ id: str
24
+ role: str # "user", "assistant", "system"
25
+ content: str
26
+ client_id: str | None = None # who sent it (None = system)
27
+ timestamp: float = field(default_factory=time.time)
28
+
29
+
30
+ class DaemonAgent:
31
+ """Shared agent session that multiple clients can connect to."""
32
+
33
+ def __init__(self, max_clients: int = 10, max_messages: int = 1000) -> None:
34
+ self._clients: dict[str, ClientSession] = {}
35
+ self._messages: list[SharedMessage] = []
36
+ self._max_clients = max_clients
37
+ self._max_messages = max_messages
38
+ self._message_counter = 0
39
+ self._event_handlers: dict[str, list[Callable]] = {}
40
+
41
+ def connect(self, client_id: str) -> ClientSession:
42
+ if len(self._clients) >= self._max_clients:
43
+ raise RuntimeError(f"Max clients ({self._max_clients}) reached")
44
+ if client_id in self._clients:
45
+ return self._clients[client_id]
46
+ session = ClientSession(client_id=client_id)
47
+ self._clients[client_id] = session
48
+ self._emit("client_connected", client_id)
49
+ return session
50
+
51
+ def disconnect(self, client_id: str) -> None:
52
+ self._clients.pop(client_id, None)
53
+ self._emit("client_disconnected", client_id)
54
+
55
+ def send_message(self, client_id: str, role: str, content: str) -> SharedMessage:
56
+ self._message_counter += 1
57
+ msg = SharedMessage(
58
+ id=f"msg_{self._message_counter}",
59
+ role=role,
60
+ content=content,
61
+ client_id=client_id,
62
+ )
63
+ self._messages.append(msg)
64
+ if len(self._messages) > self._max_messages:
65
+ self._messages = self._messages[-self._max_messages:]
66
+ self._emit("message", msg)
67
+ return msg
68
+
69
+ def get_messages(self, since_id: str | None = None) -> list[SharedMessage]:
70
+ if since_id is None:
71
+ return list(self._messages)
72
+ idx = next(
73
+ (i for i, m in enumerate(self._messages) if m.id == since_id),
74
+ 0,
75
+ )
76
+ return self._messages[idx + 1:]
77
+
78
+ def subscribe(self, client_id: str, event: str) -> None:
79
+ session = self._clients.get(client_id)
80
+ if session:
81
+ session.subscriptions.add(event)
82
+
83
+ def on_event(self, event: str, handler: Callable) -> None:
84
+ self._event_handlers.setdefault(event, []).append(handler)
85
+
86
+ def _emit(self, event: str, data: Any) -> None:
87
+ for handler in self._event_handlers.get(event, []):
88
+ try:
89
+ handler(data)
90
+ except Exception:
91
+ pass
92
+
93
+ @property
94
+ def client_count(self) -> int:
95
+ return len(self._clients)
96
+
97
+ @property
98
+ def message_count(self) -> int:
99
+ return len(self._messages)
@@ -0,0 +1,151 @@
1
+ """B53. Dependency Graph Agent Orchestration.
2
+
3
+ Orchestrate agents as a dependency graph with parallel fanout,
4
+ iterative cycles, and scratchboard shared memory.
5
+ Source: agentflow pattern.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum, auto
11
+ from typing import Any, Callable
12
+
13
+
14
+ class NodeStatus(Enum):
15
+ PENDING = auto()
16
+ RUNNING = auto()
17
+ DONE = auto()
18
+ FAILED = auto()
19
+ SKIPPED = auto()
20
+
21
+
22
+ @dataclass
23
+ class DAGNode:
24
+ id: str
25
+ execute: Callable[[dict], Any]
26
+ depends_on: list[str] = field(default_factory=list)
27
+ status: NodeStatus = NodeStatus.PENDING
28
+ result: Any = None
29
+ error: str | None = None
30
+
31
+
32
+ @dataclass
33
+ class DAGEdge:
34
+ source: str
35
+ target: str
36
+
37
+
38
+ @dataclass
39
+ class CycleSpec:
40
+ """Iterative cycle: repeat until success criteria met."""
41
+ nodes: list[str]
42
+ max_iterations: int = 5
43
+ success_check: Callable[[dict], bool] | None = None
44
+
45
+
46
+ class DependencyGraph:
47
+ """DAG executor with parallel fanout and iterative cycles."""
48
+
49
+ def __init__(self) -> None:
50
+ self._nodes: dict[str, DAGNode] = {}
51
+ self._edges: list[DAGEdge] = []
52
+ self._cycles: list[CycleSpec] = []
53
+ self._scratchboard: dict[str, Any] = {}
54
+
55
+ def add_node(self, node: DAGNode) -> None:
56
+ self._nodes[node.id] = node
57
+
58
+ def add_edge(self, source: str, target: str) -> None:
59
+ self._edges.append(DAGEdge(source, target))
60
+
61
+ def add_cycle(self, cycle: CycleSpec) -> None:
62
+ self._cycles.append(cycle)
63
+
64
+ def topological_order(self) -> list[str]:
65
+ """Kahn's algorithm for topological sort."""
66
+ in_degree: dict[str, int] = {n: 0 for n in self._nodes}
67
+ adj: dict[str, list[str]] = {n: [] for n in self._nodes}
68
+ for e in self._edges:
69
+ adj[e.source].append(e.target)
70
+ in_degree[e.target] += 1
71
+
72
+ queue = [n for n, d in in_degree.items() if d == 0]
73
+ order: list[str] = []
74
+ while queue:
75
+ node = queue.pop(0)
76
+ order.append(node)
77
+ for neighbor in adj[node]:
78
+ in_degree[neighbor] -= 1
79
+ if in_degree[neighbor] == 0:
80
+ queue.append(neighbor)
81
+
82
+ if len(order) != len(self._nodes):
83
+ raise ValueError("Cycle detected in DAG")
84
+ return order
85
+
86
+ def _get_ready_nodes(self) -> list[str]:
87
+ """Nodes whose dependencies are all DONE."""
88
+ ready = []
89
+ for nid, node in self._nodes.items():
90
+ if node.status != NodeStatus.PENDING:
91
+ continue
92
+ deps = self._dependencies(nid)
93
+ if all(self._nodes[d].status == NodeStatus.DONE for d in deps):
94
+ ready.append(nid)
95
+ return ready
96
+
97
+ def _dependencies(self, node_id: str) -> list[str]:
98
+ return [e.source for e in self._edges if e.target == node_id]
99
+
100
+ def run(self, initial_context: dict | None = None) -> dict[str, Any]:
101
+ """Execute the DAG, returning results for all nodes."""
102
+ ctx = initial_context or {}
103
+ ctx.update(self._scratchboard)
104
+
105
+ for _ in range(len(self._nodes) * 2): # safety limit
106
+ ready = self._get_ready_nodes()
107
+ if not ready:
108
+ break
109
+ for nid in ready:
110
+ node = self._nodes[nid]
111
+ node.status = NodeStatus.RUNNING
112
+ try:
113
+ dep_results = {d: self._nodes[d].result for d in self._dependencies(nid)}
114
+ ctx.setdefault("_results", {}).update(dep_results)
115
+ node.result = node.execute(ctx)
116
+ if node.result is not None:
117
+ ctx[nid] = node.result
118
+ node.status = NodeStatus.DONE
119
+ except Exception as e:
120
+ node.error = str(e)
121
+ node.status = NodeStatus.FAILED
122
+ ctx.setdefault("_errors", []).append({"node": nid, "error": str(e)})
123
+
124
+ # Handle cycles
125
+ for cycle in self._cycles:
126
+ for iteration in range(cycle.max_iterations):
127
+ for nid in cycle.nodes:
128
+ node = self._nodes[nid]
129
+ node.status = NodeStatus.PENDING
130
+ for nid in cycle.nodes:
131
+ node = self._nodes[nid]
132
+ node.status = NodeStatus.RUNNING
133
+ try:
134
+ dep_results = {d: self._nodes[d].result for d in self._dependencies(nid)}
135
+ ctx.setdefault("_results", {}).update(dep_results)
136
+ node.result = node._DAGNode__wrapped_execute(ctx) if hasattr(node, "_DAGNode__wrapped_execute") else node.execute(ctx)
137
+ if node.result is not None:
138
+ ctx[nid] = node.result
139
+ node.status = NodeStatus.DONE
140
+ except Exception:
141
+ node.status = NodeStatus.FAILED
142
+
143
+ if cycle.success_check and cycle.success_check(ctx):
144
+ break
145
+
146
+ self._scratchboard.update(ctx)
147
+ return dict(ctx)
148
+
149
+ @property
150
+ def scratchboard(self) -> dict[str, Any]:
151
+ return dict(self._scratchboard)
@@ -0,0 +1,155 @@
1
+ """B62. Sub-Question Decomposition + Iterative Gap-Filling.
2
+
3
+ Decompose a complex query into sub-questions, search for each,
4
+ identify gaps, and search again until coverage is sufficient.
5
+ Source: sibyl pattern.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum, auto
11
+ from typing import Callable
12
+
13
+
14
+ class GapType(Enum):
15
+ MISSING = auto() # no answer found
16
+ SHALLOW = auto() # answer too brief
17
+ STALE = auto() # answer references old info
18
+ CONTRADICTORY = auto() # conflicting answers
19
+
20
+
21
+ @dataclass
22
+ class SubQuestion:
23
+ id: str
24
+ question: str
25
+ search_queries: list[str] = field(default_factory=list)
26
+ answer: str = ""
27
+ sources: list[str] = field(default_factory=list)
28
+ confidence: float = 0.0
29
+ gaps: list[GapType] = field(default_factory=list)
30
+
31
+
32
+ @dataclass
33
+ class DecompositionResult:
34
+ original_query: str
35
+ sub_questions: list[SubQuestion] = field(default_factory=list)
36
+ iterations: int = 0
37
+ coverage_score: float = 0.0
38
+ summary: str = ""
39
+
40
+
41
+ class QueryDecomposer:
42
+ """Decompose a complex query into sub-questions."""
43
+
44
+ QUESTION_PREFIXES = [
45
+ "What is", "How does", "Why is", "When was", "Where is",
46
+ "Who is", "What are the key features of",
47
+ "What are the limitations of", "How is X different from",
48
+ ]
49
+
50
+ def decompose(self, query: str, max_sub_questions: int = 5) -> list[SubQuestion]:
51
+ """Split a complex query into focused sub-questions."""
52
+ # Simple heuristic: split on conjunctions and question marks
53
+ parts = query.replace(" and ", "|").replace(" vs ", "|").split("|")
54
+ sub_qs: list[SubQuestion] = []
55
+ for i, part in enumerate(parts):
56
+ part = part.strip().rstrip("?")
57
+ if not part:
58
+ continue
59
+ sq = SubQuestion(id=f"sq_{i+1}", question=part)
60
+ # Generate search queries
61
+ sq.search_queries = [part, f"{part} overview", f"{part} explained"]
62
+ sub_qs.append(sq)
63
+ if len(sub_qs) >= max_sub_questions:
64
+ break
65
+
66
+ if not sub_qs:
67
+ sub_qs.append(SubQuestion(id="sq_1", question=query, search_queries=[query]))
68
+
69
+ return sub_qs
70
+
71
+
72
+ class GapFiller:
73
+ """Identify gaps in answers and generate follow-up searches."""
74
+
75
+ def identify_gaps(self, sq: SubQuestion) -> list[GapType]:
76
+ gaps: list[GapType] = []
77
+ if not sq.answer:
78
+ gaps.append(GapType.MISSING)
79
+ elif len(sq.answer) < 100:
80
+ gaps.append(GapType.SHALLOW)
81
+ if sq.confidence < 0.5:
82
+ gaps.append(GapType.MISSING)
83
+ return gaps
84
+
85
+ def generate_followup(self, sq: SubQuestion) -> list[str]:
86
+ """Generate follow-up search queries for gaps."""
87
+ followups: list[str] = []
88
+ for gap in sq.gaps:
89
+ if gap == GapType.MISSING:
90
+ followups.append(f"{sq.question} detailed explanation")
91
+ followups.append(f"{sq.question} examples")
92
+ elif gap == GapType.SHALLOW:
93
+ followups.append(f"{sq.question} in depth analysis")
94
+ elif gap == GapType.STALE:
95
+ followups.append(f"{sq.question} 2025 2026 latest")
96
+ return followups
97
+
98
+
99
+ class DeepResearchEngine:
100
+ """Orchestrate sub-question decomposition with iterative gap-filling."""
101
+
102
+ def __init__(self, search_fn: Callable[[str], list[tuple[str, str]]],
103
+ max_iterations: int = 3, coverage_threshold: float = 0.8) -> None:
104
+ self._search = search_fn
105
+ self._max_iterations = max_iterations
106
+ self._coverage_threshold = coverage_threshold
107
+ self._decomposer = QueryDecomposer()
108
+ self._gap_filler = GapFiller()
109
+
110
+ def run(self, query: str) -> DecompositionResult:
111
+ result = DecompositionResult(original_query=query)
112
+ result.sub_questions = self._decomposer.decompose(query)
113
+
114
+ for iteration in range(self._max_iterations):
115
+ result.iterations = iteration + 1
116
+
117
+ for sq in result.sub_questions:
118
+ if sq.confidence >= 0.7:
119
+ continue
120
+ # Search for each query
121
+ for search_q in sq.search_queries:
122
+ results = self._search(search_q)
123
+ for title, content in results:
124
+ sq.sources.append(title)
125
+ if not sq.answer:
126
+ sq.answer = content
127
+ else:
128
+ sq.answer += "\n\n" + content
129
+ if results:
130
+ sq.confidence = min(1.0, sq.confidence + 0.3)
131
+
132
+ # Check for gaps
133
+ sq.gaps = self._gap_filler.identify_gaps(sq)
134
+ if sq.gaps:
135
+ followups = self._gap_filler.generate_followup(sq)
136
+ sq.search_queries.extend(followups)
137
+
138
+ # Compute coverage
139
+ answered = sum(1 for sq in result.sub_questions if sq.confidence >= 0.7)
140
+ result.coverage_score = answered / len(result.sub_questions) if result.sub_questions else 0.0
141
+
142
+ if result.coverage_score >= self._coverage_threshold:
143
+ break
144
+
145
+ result.summary = self._synthesize(result)
146
+ return result
147
+
148
+ def _synthesize(self, result: DecompositionResult) -> str:
149
+ lines = [f"# Research: {result.original_query}", ""]
150
+ for sq in result.sub_questions:
151
+ lines.append(f"## {sq.question}")
152
+ lines.append(sq.answer[:500] if sq.answer else "(no answer found)")
153
+ lines.append("")
154
+ lines.append(f"Coverage: {result.coverage_score:.0%} after {result.iterations} iterations")
155
+ return "\n".join(lines)
@@ -0,0 +1,78 @@
1
+ """H27 — Degenerate Solution Detection.
2
+
3
+ Catch solutions that precompute answers without required constructs.
4
+ Mined from GLOSSOPETRAE gradeStructured() — degenerate_precompute detection.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import ast
9
+ from dataclasses import dataclass
10
+
11
+
12
+ @dataclass
13
+ class DegenerateReport:
14
+ """Report on whether a solution is degenerate."""
15
+
16
+ is_degenerate: bool
17
+ reason: str = ""
18
+ has_loops: bool = False
19
+ has_functions: bool = False
20
+ has_conditionals: bool = False
21
+ literal_count: int = 0
22
+
23
+ def to_dict(self) -> dict[str, object]:
24
+ return {
25
+ "is_degenerate": self.is_degenerate,
26
+ "reason": self.reason,
27
+ "has_loops": self.has_loops,
28
+ "has_functions": self.has_functions,
29
+ "has_conditionals": self.has_conditionals,
30
+ "literal_count": self.literal_count,
31
+ }
32
+
33
+
34
+ class DegenerateDetector:
35
+ """Detect programs that precompute answers without algorithmic structure."""
36
+
37
+ def __init__(self, require_loops: bool = True, require_functions: bool = True) -> None:
38
+ self.require_loops = require_loops
39
+ self.require_functions = require_functions
40
+
41
+ def analyze(self, code: str) -> DegenerateReport:
42
+ try:
43
+ tree = ast.parse(code)
44
+ except SyntaxError as e:
45
+ return DegenerateReport(
46
+ is_degenerate=True,
47
+ reason=f"Syntax error: {e}",
48
+ )
49
+ has_loops = False
50
+ has_functions = False
51
+ has_conditionals = False
52
+ literal_count = 0
53
+ for node in ast.walk(tree):
54
+ if isinstance(node, (ast.For, ast.While)):
55
+ has_loops = True
56
+ elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
57
+ has_functions = True
58
+ elif isinstance(node, ast.If):
59
+ has_conditionals = True
60
+ elif isinstance(node, ast.Constant):
61
+ literal_count += 1
62
+ missing = []
63
+ if self.require_loops and not has_loops:
64
+ missing.append("loops")
65
+ if self.require_functions and not has_functions:
66
+ missing.append("functions")
67
+ is_degenerate = len(missing) > 0 and literal_count > 0
68
+ reason = ""
69
+ if is_degenerate:
70
+ reason = f"Solution has no {' or '.join(missing)} but contains {literal_count} literals — likely precomputed"
71
+ return DegenerateReport(
72
+ is_degenerate=is_degenerate,
73
+ reason=reason,
74
+ has_loops=has_loops,
75
+ has_functions=has_functions,
76
+ has_conditionals=has_conditionals,
77
+ literal_count=literal_count,
78
+ )
@@ -0,0 +1,92 @@
1
+ """H30 — Delta Reporting.
2
+
3
+ Report what changed since last assessment, not full reports.
4
+ Mined from T3MP3ST VISION.md Vector 4.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import time
9
+ from dataclasses import dataclass, field
10
+ from typing import Any
11
+
12
+
13
+ @dataclass
14
+ class DeltaEntry:
15
+ """A single change in a delta report."""
16
+
17
+ change_type: str # "added", "removed", "changed"
18
+ target_id: str
19
+ old_value: Any = None
20
+ new_value: Any = None
21
+ timestamp: float = field(default_factory=time.time)
22
+
23
+ def to_dict(self) -> dict[str, Any]:
24
+ return {
25
+ "change_type": self.change_type,
26
+ "target_id": self.target_id,
27
+ "old_value": self.old_value,
28
+ "new_value": self.new_value,
29
+ "timestamp": self.timestamp,
30
+ }
31
+
32
+
33
+ @dataclass
34
+ class DeltaReport:
35
+ """A report of changes between two states."""
36
+
37
+ entries: list[DeltaEntry] = field(default_factory=list)
38
+ generated_at: float = field(default_factory=time.time)
39
+
40
+ @property
41
+ def added(self) -> list[DeltaEntry]:
42
+ return [e for e in self.entries if e.change_type == "added"]
43
+
44
+ @property
45
+ def removed(self) -> list[DeltaEntry]:
46
+ return [e for e in self.entries if e.change_type == "removed"]
47
+
48
+ @property
49
+ def changed(self) -> list[DeltaEntry]:
50
+ return [e for e in self.entries if e.change_type == "changed"]
51
+
52
+ @property
53
+ def is_empty(self) -> bool:
54
+ return len(self.entries) == 0
55
+
56
+ def summary(self) -> str:
57
+ return (
58
+ f"Delta: +{len(self.added)} added, -{len(self.removed)} removed, "
59
+ f"~{len(self.changed)} changed"
60
+ )
61
+
62
+ def to_dict(self) -> dict[str, Any]:
63
+ return {
64
+ "entries": [e.to_dict() for e in self.entries],
65
+ "generated_at": self.generated_at,
66
+ "summary": self.summary(),
67
+ }
68
+
69
+
70
+ class DeltaReporter:
71
+ """Compute deltas between two state snapshots."""
72
+
73
+ @staticmethod
74
+ def compute(
75
+ previous: dict[str, Any],
76
+ current: dict[str, Any],
77
+ ) -> DeltaReport:
78
+ """Compute delta between two state dicts keyed by ID."""
79
+ entries: list[DeltaEntry] = []
80
+ prev_keys = set(previous.keys())
81
+ curr_keys = set(current.keys())
82
+ # Added
83
+ for key in sorted(curr_keys - prev_keys):
84
+ entries.append(DeltaEntry("added", key, new_value=current[key]))
85
+ # Removed
86
+ for key in sorted(prev_keys - curr_keys):
87
+ entries.append(DeltaEntry("removed", key, old_value=previous[key]))
88
+ # Changed
89
+ for key in sorted(prev_keys & curr_keys):
90
+ if previous[key] != current[key]:
91
+ entries.append(DeltaEntry("changed", key, previous[key], current[key]))
92
+ return DeltaReport(entries=entries)