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.
- algo_cli/__init__.py +3 -0
- algo_cli/__main__.py +7 -0
- algo_cli/_internal/__init__.py +12 -0
- algo_cli/_internal/policy_chain.py +259 -0
- algo_cli/action_registry.py +1047 -0
- algo_cli/agent_blocks.py +550 -0
- algo_cli/agent_pipeline.py +1457 -0
- algo_cli/agent_threads.py +308 -0
- algo_cli/animations.py +316 -0
- algo_cli/cache_admission.py +209 -0
- algo_cli/capability_mask.py +66 -0
- algo_cli/chat_protocol.py +116 -0
- algo_cli/chatgpt_auth.py +510 -0
- algo_cli/chatgpt_client.py +657 -0
- algo_cli/code_rag.py +479 -0
- algo_cli/config.py +651 -0
- algo_cli/context_budget.py +679 -0
- algo_cli/credential_helpers.py +315 -0
- algo_cli/deliberation.py +29 -0
- algo_cli/display.py +1470 -0
- algo_cli/evals/__init__.py +21 -0
- algo_cli/evals/algorithm_effectiveness.py +560 -0
- algo_cli/evals/competitive_harness_rating.py +702 -0
- algo_cli/evals/cot_quality.py +220 -0
- algo_cli/evals/harness_retrieval_benchmark.py +401 -0
- algo_cli/evals/performance_regression.py +136 -0
- algo_cli/evals/scorecard_grading.py +308 -0
- algo_cli/evals/session_distribution.py +84 -0
- algo_cli/execution_guardrails.py +806 -0
- algo_cli/extensions_manifest.py +84 -0
- algo_cli/git_evidence.py +227 -0
- algo_cli/google_workspace.py +407 -0
- algo_cli/google_workspace_auth.py +523 -0
- algo_cli/harness.py +2587 -0
- algo_cli/identity.py +557 -0
- algo_cli/index_compute_lab.py +228 -0
- algo_cli/inference_harness.py +70 -0
- algo_cli/intelligence/__init__.py +1103 -0
- algo_cli/intelligence/acrobat_config.py +307 -0
- algo_cli/intelligence/acrobat_manifests.py +338 -0
- algo_cli/intelligence/acrobat_models.py +195 -0
- algo_cli/intelligence/acrobat_pipeline.py +295 -0
- algo_cli/intelligence/acrobat_runtime.py +302 -0
- algo_cli/intelligence/acrobat_security.py +261 -0
- algo_cli/intelligence/acrobat_workflows.py +226 -0
- algo_cli/intelligence/actionability.py +165 -0
- algo_cli/intelligence/adversarial_audit.py +136 -0
- algo_cli/intelligence/agent_arena.py +92 -0
- algo_cli/intelligence/agent_benchmark.py +236 -0
- algo_cli/intelligence/agent_runtime.py +171 -0
- algo_cli/intelligence/agents_as_tools.py +70 -0
- algo_cli/intelligence/artifact_binding.py +80 -0
- algo_cli/intelligence/autonomous_engineer.py +1976 -0
- algo_cli/intelligence/backpressure.py +99 -0
- algo_cli/intelligence/bloom_filter.py +186 -0
- algo_cli/intelligence/bonferroni.py +66 -0
- algo_cli/intelligence/boundary_compaction.py +98 -0
- algo_cli/intelligence/catalog_verifier.py +172 -0
- algo_cli/intelligence/cavecrew.py +118 -0
- algo_cli/intelligence/changelog.py +176 -0
- algo_cli/intelligence/checkpoint_resume.py +92 -0
- algo_cli/intelligence/circuit_breaker.py +88 -0
- algo_cli/intelligence/clarification_gate.py +101 -0
- algo_cli/intelligence/code_graph.py +180 -0
- algo_cli/intelligence/coderank.py +97 -0
- algo_cli/intelligence/consistent_hash.py +150 -0
- algo_cli/intelligence/consortium_synthesis.py +139 -0
- algo_cli/intelligence/construction/__init__.py +241 -0
- algo_cli/intelligence/construction/common.py +273 -0
- algo_cli/intelligence/construction/documents.py +496 -0
- algo_cli/intelligence/construction/labor_units.py +1395 -0
- algo_cli/intelligence/construction/payments.py +470 -0
- algo_cli/intelligence/construction/risk.py +784 -0
- algo_cli/intelligence/content_extractor.py +132 -0
- algo_cli/intelligence/context_adaptive.py +102 -0
- algo_cli/intelligence/context_ops.py +95 -0
- algo_cli/intelligence/count_min.py +145 -0
- algo_cli/intelligence/cow_state.py +103 -0
- algo_cli/intelligence/critic_loop.py +119 -0
- algo_cli/intelligence/cross_source.py +113 -0
- algo_cli/intelligence/daemon_mode.py +99 -0
- algo_cli/intelligence/dag_orchestration.py +151 -0
- algo_cli/intelligence/deep_research.py +155 -0
- algo_cli/intelligence/degenerate_detector.py +78 -0
- algo_cli/intelligence/delta_report.py +92 -0
- algo_cli/intelligence/discovery_event_log.py +92 -0
- algo_cli/intelligence/document_ingest.py +298 -0
- algo_cli/intelligence/dual_layer_validate.py +151 -0
- algo_cli/intelligence/echo_fidelity.py +73 -0
- algo_cli/intelligence/ema_tuning.py +104 -0
- algo_cli/intelligence/event_log.py +92 -0
- algo_cli/intelligence/evidence_graph.py +114 -0
- algo_cli/intelligence/extension_host.py +162 -0
- algo_cli/intelligence/extension_manifest.py +115 -0
- algo_cli/intelligence/falsification_suite.py +178 -0
- algo_cli/intelligence/finance/__init__.py +169 -0
- algo_cli/intelligence/finance/anomalies.py +135 -0
- algo_cli/intelligence/finance/ap_ar.py +351 -0
- algo_cli/intelligence/finance/cash.py +162 -0
- algo_cli/intelligence/finance/close.py +332 -0
- algo_cli/intelligence/finance/common.py +244 -0
- algo_cli/intelligence/finance/construction.py +135 -0
- algo_cli/intelligence/finance/controls.py +172 -0
- algo_cli/intelligence/finance/evidence.py +119 -0
- algo_cli/intelligence/finance/exceptions.py +157 -0
- algo_cli/intelligence/finance/reconciliations.py +254 -0
- algo_cli/intelligence/finance/revenue.py +109 -0
- algo_cli/intelligence/finance/tax.py +74 -0
- algo_cli/intelligence/finance/workpapers.py +111 -0
- algo_cli/intelligence/finding_record.py +120 -0
- algo_cli/intelligence/flow_dag.py +267 -0
- algo_cli/intelligence/gatherer.py +223 -0
- algo_cli/intelligence/golden_master.py +98 -0
- algo_cli/intelligence/graph_rag.py +195 -0
- algo_cli/intelligence/group_chat.py +143 -0
- algo_cli/intelligence/hash_dedup.py +145 -0
- algo_cli/intelligence/hyperloglog.py +128 -0
- algo_cli/intelligence/incremental_index.py +316 -0
- algo_cli/intelligence/index_store.py +16 -0
- algo_cli/intelligence/iteration_plan.py +133 -0
- algo_cli/intelligence/kernel_plugins.py +167 -0
- algo_cli/intelligence/lesson_catalog.py +135 -0
- algo_cli/intelligence/llm_fallback.py +169 -0
- algo_cli/intelligence/log2_histogram.py +267 -0
- algo_cli/intelligence/lsp_integration.py +147 -0
- algo_cli/intelligence/memory_evolution.py +117 -0
- algo_cli/intelligence/minhash_lsh.py +182 -0
- algo_cli/intelligence/multi_model_score.py +174 -0
- algo_cli/intelligence/multi_tier_grade.py +211 -0
- algo_cli/intelligence/negative_controls.py +113 -0
- algo_cli/intelligence/numeric_clamp.py +63 -0
- algo_cli/intelligence/occ_editor.py +66 -0
- algo_cli/intelligence/output_normalize.py +112 -0
- algo_cli/intelligence/parallel_delegation.py +98 -0
- algo_cli/intelligence/parallel_fanout.py +104 -0
- algo_cli/intelligence/permission_modes.py +105 -0
- algo_cli/intelligence/pre_push_gate.py +68 -0
- algo_cli/intelligence/prefetch.py +171 -0
- algo_cli/intelligence/process_framework.py +217 -0
- algo_cli/intelligence/project_graph.py +387 -0
- algo_cli/intelligence/query_expansion.py +146 -0
- algo_cli/intelligence/ralph_loop.py +117 -0
- algo_cli/intelligence/rate_limiter.py +153 -0
- algo_cli/intelligence/refactor_transaction.py +94 -0
- algo_cli/intelligence/research_workspace.py +108 -0
- algo_cli/intelligence/retraction_ledger.py +72 -0
- algo_cli/intelligence/saga_pattern.py +88 -0
- algo_cli/intelligence/session_fork.py +100 -0
- algo_cli/intelligence/shadow_editor.py +67 -0
- algo_cli/intelligence/shell_session.py +213 -0
- algo_cli/intelligence/source_registry.py +143 -0
- algo_cli/intelligence/spawn_scales.py +99 -0
- algo_cli/intelligence/stat_stability.py +104 -0
- algo_cli/intelligence/structural_validator.py +148 -0
- algo_cli/intelligence/subagent_spawner.py +111 -0
- algo_cli/intelligence/symmetric_verify.py +70 -0
- algo_cli/intelligence/task_classifier.py +129 -0
- algo_cli/intelligence/team_execution.py +122 -0
- algo_cli/intelligence/tiered_access.py +121 -0
- algo_cli/intelligence/utility_registry.py +159 -0
- algo_cli/intuition_engine.py +560 -0
- algo_cli/intuition_injector.py +82 -0
- algo_cli/kernels/__init__.py +5 -0
- algo_cli/kernels/manifest.py +763 -0
- algo_cli/main.py +3903 -0
- algo_cli/memory_candidates.py +541 -0
- algo_cli/memory_echo_veil.py +394 -0
- algo_cli/memory_runtime.py +112 -0
- algo_cli/model_info.py +548 -0
- algo_cli/model_profile.py +160 -0
- algo_cli/model_routing.py +74 -0
- algo_cli/oneshot.py +331 -0
- algo_cli/perf_telemetry.py +389 -0
- algo_cli/plugins.py +245 -0
- algo_cli/private_event_store.py +654 -0
- algo_cli/quantization/__init__.py +24 -0
- algo_cli/quantization/lloyd_max.py +98 -0
- algo_cli/quantization/turbo_quant.py +308 -0
- algo_cli/reasoning/__init__.py +46 -0
- algo_cli/reasoning/combinatorial.py +356 -0
- algo_cli/reasoning/graph_of_thought.py +297 -0
- algo_cli/reasoning/mcts.py +220 -0
- algo_cli/reasoning/neuro_symbolic.py +250 -0
- algo_cli/reasoning/react.py +246 -0
- algo_cli/reasoning/reflexion.py +225 -0
- algo_cli/reasoning/tree_of_thought.py +241 -0
- algo_cli/reasoning_bridge.py +150 -0
- algo_cli/reconciliation.py +284 -0
- algo_cli/reflex.py +385 -0
- algo_cli/resources/docs/ALGO.md +13958 -0
- algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
- algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
- algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
- algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
- algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
- algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
- algo_cli/resources/docs/main-split-map.md +35 -0
- algo_cli/resources/docs/privacy-and-context.md +48 -0
- algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
- algo_cli/resources/skills/README.md +26 -0
- algo_cli/resources/skills/algo-cli.md +59 -0
- algo_cli/resources/skills/edit-file-precision.md +49 -0
- algo_cli/resources/skills/harness-search-first.md +47 -0
- algo_cli/resources/skills/memory-recall-ritual.md +51 -0
- algo_cli/resources/skills/qol-algorithms.md +224 -0
- algo_cli/resources/skills/smart-error-recovery.md +56 -0
- algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
- algo_cli/retrieval_algorithms.py +127 -0
- algo_cli/runtime_qos.py +236 -0
- algo_cli/runtime_services.py +320 -0
- algo_cli/session_commands.py +95 -0
- algo_cli/session_mode.py +113 -0
- algo_cli/skills.py +430 -0
- algo_cli/slash_dispatch.py +1265 -0
- algo_cli/small_context.py +206 -0
- algo_cli/spawn_budget.py +89 -0
- algo_cli/task_ledger.py +84 -0
- algo_cli/task_router.py +197 -0
- algo_cli/tool_context.py +94 -0
- algo_cli/tool_contract.py +99 -0
- algo_cli/tool_policy.py +357 -0
- algo_cli/tool_runtime.py +647 -0
- algo_cli/tools.py +3056 -0
- algo_cli/url_scheme.py +174 -0
- algo_cli/verify.py +154 -0
- algo_cli/version_manifest.py +178 -0
- algo_cli/vision_screenshot_verify.py +76 -0
- algo_cli/workspace_resolver.py +68 -0
- algo_cli/x_account.py +209 -0
- algo_cli/xai_auth.py +374 -0
- algo_cli/xai_client.py +600 -0
- algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
- algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
- algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
- algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
- algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
- ollama_cli/__init__.py +67 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""H13 — Feedback-Driven Parameter Tuning (EMA).
|
|
2
|
+
|
|
3
|
+
Exponential moving average tuning: user feedback (binary ratings) updates
|
|
4
|
+
an EMA that adjusts harness parameters over time. Includes sample-count
|
|
5
|
+
gating (no adjustments until minimum samples collected) and bounded history.
|
|
6
|
+
|
|
7
|
+
Source: G0DM0D3 AutoTune.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections import deque
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
MAX_HISTORY = 500
|
|
17
|
+
MIN_SAMPLES_TO_APPLY = 3
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class TuningState:
|
|
22
|
+
"""Snapshot of the EMA tuner state."""
|
|
23
|
+
|
|
24
|
+
ema: float
|
|
25
|
+
samples: int
|
|
26
|
+
applied: bool
|
|
27
|
+
history_size: int
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EMATuner:
|
|
31
|
+
"""EMA-based parameter tuner with sample-count gating."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
param_name: str,
|
|
36
|
+
initial_value: float = 0.5,
|
|
37
|
+
alpha: float = 0.3,
|
|
38
|
+
min_samples: int = MIN_SAMPLES_TO_APPLY,
|
|
39
|
+
max_history: int = MAX_HISTORY,
|
|
40
|
+
) -> None:
|
|
41
|
+
if not 0.0 < alpha <= 1.0:
|
|
42
|
+
raise ValueError(f"alpha must be in (0, 1], got {alpha}")
|
|
43
|
+
self.param_name = param_name
|
|
44
|
+
self.alpha = alpha
|
|
45
|
+
self.min_samples = min_samples
|
|
46
|
+
self.max_history = max_history
|
|
47
|
+
self._ema = initial_value
|
|
48
|
+
self._samples = 0
|
|
49
|
+
self._history: deque[float] = deque(maxlen=max_history)
|
|
50
|
+
|
|
51
|
+
def update(self, rating: float) -> float:
|
|
52
|
+
"""Update EMA with a new rating (0.0 to 1.0).
|
|
53
|
+
|
|
54
|
+
Returns the updated EMA value.
|
|
55
|
+
"""
|
|
56
|
+
if not 0.0 <= rating <= 1.0:
|
|
57
|
+
raise ValueError(f"rating must be in [0, 1], got {rating}")
|
|
58
|
+
self._samples += 1
|
|
59
|
+
self._history.append(rating)
|
|
60
|
+
self._ema = self.alpha * rating + (1 - self.alpha) * self._ema
|
|
61
|
+
return self._ema
|
|
62
|
+
|
|
63
|
+
def update_binary(self, positive: bool) -> float:
|
|
64
|
+
"""Update with a binary rating (True=1.0, False=0.0)."""
|
|
65
|
+
return self.update(1.0 if positive else 0.0)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def ema(self) -> float:
|
|
69
|
+
return self._ema
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def samples(self) -> int:
|
|
73
|
+
return self._samples
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def can_apply(self) -> bool:
|
|
77
|
+
"""True when enough samples have been collected to apply tuning."""
|
|
78
|
+
return self._samples >= self.min_samples
|
|
79
|
+
|
|
80
|
+
def apply(self, params: dict[str, Any]) -> dict[str, Any]:
|
|
81
|
+
"""Apply the tuned value to a params dict if enough samples exist.
|
|
82
|
+
|
|
83
|
+
Returns a new dict with the parameter updated.
|
|
84
|
+
"""
|
|
85
|
+
if not self.can_apply:
|
|
86
|
+
return params
|
|
87
|
+
result = dict(params)
|
|
88
|
+
result[self.param_name] = self._ema
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
def state(self) -> TuningState:
|
|
92
|
+
"""Return a snapshot of the current state."""
|
|
93
|
+
return TuningState(
|
|
94
|
+
ema=round(self._ema, 6),
|
|
95
|
+
samples=self._samples,
|
|
96
|
+
applied=self.can_apply,
|
|
97
|
+
history_size=len(self._history),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def reset(self) -> None:
|
|
101
|
+
"""Reset to initial state."""
|
|
102
|
+
self._ema = 0.5
|
|
103
|
+
self._samples = 0
|
|
104
|
+
self._history.clear()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""H4 — Harness Discovery Event Log.
|
|
2
|
+
|
|
3
|
+
Structured events for the full discovery lifecycle.
|
|
4
|
+
Mined from T3MP3ST EventEmitter (finding:discovered, finding:retracted, etc.).
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Callable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EventType(str, Enum):
|
|
15
|
+
DISCOVERED = "discovered"
|
|
16
|
+
PROPOSED = "proposed"
|
|
17
|
+
VERIFIED = "verified"
|
|
18
|
+
RETRACTED = "retracted"
|
|
19
|
+
DEPRECATED = "deprecated"
|
|
20
|
+
PROMOTED = "promoted"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Event:
|
|
25
|
+
"""A single discovery event."""
|
|
26
|
+
|
|
27
|
+
event_type: EventType
|
|
28
|
+
target_id: str
|
|
29
|
+
timestamp: float = field(default_factory=time.time)
|
|
30
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
31
|
+
|
|
32
|
+
def to_dict(self) -> dict[str, Any]:
|
|
33
|
+
return {
|
|
34
|
+
"event_type": self.event_type.value,
|
|
35
|
+
"target_id": self.target_id,
|
|
36
|
+
"timestamp": self.timestamp,
|
|
37
|
+
"metadata": dict(self.metadata),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class EventLogger:
|
|
42
|
+
"""Append-only event log with subscription support."""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
self._events: list[Event] = []
|
|
46
|
+
self._subscribers: list[tuple[Callable[[Event], None], EventType | None]] = []
|
|
47
|
+
|
|
48
|
+
def emit(
|
|
49
|
+
self,
|
|
50
|
+
event_type: EventType,
|
|
51
|
+
target_id: str,
|
|
52
|
+
metadata: dict[str, Any] | None = None,
|
|
53
|
+
) -> Event:
|
|
54
|
+
"""Emit an event and notify subscribers."""
|
|
55
|
+
event = Event(
|
|
56
|
+
event_type=event_type,
|
|
57
|
+
target_id=target_id,
|
|
58
|
+
metadata=metadata or {},
|
|
59
|
+
)
|
|
60
|
+
self._events.append(event)
|
|
61
|
+
for callback, filter_type in self._subscribers:
|
|
62
|
+
if filter_type is None or filter_type == event_type:
|
|
63
|
+
callback(event)
|
|
64
|
+
return event
|
|
65
|
+
|
|
66
|
+
def subscribe(
|
|
67
|
+
self,
|
|
68
|
+
callback: Callable[[Event], None],
|
|
69
|
+
event_type: EventType | None = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
"""Subscribe to events. Optionally filter by event type."""
|
|
72
|
+
self._subscribers.append((callback, event_type))
|
|
73
|
+
|
|
74
|
+
def query(
|
|
75
|
+
self,
|
|
76
|
+
event_type: EventType | None = None,
|
|
77
|
+
target_id: str | None = None,
|
|
78
|
+
) -> list[Event]:
|
|
79
|
+
"""Query events by type and/or target."""
|
|
80
|
+
results = self._events
|
|
81
|
+
if event_type is not None:
|
|
82
|
+
results = [e for e in results if e.event_type == event_type]
|
|
83
|
+
if target_id is not None:
|
|
84
|
+
results = [e for e in results if e.target_id == target_id]
|
|
85
|
+
return list(results)
|
|
86
|
+
|
|
87
|
+
def count(self) -> int:
|
|
88
|
+
return len(self._events)
|
|
89
|
+
|
|
90
|
+
def clear(self) -> None:
|
|
91
|
+
self._events.clear()
|
|
92
|
+
self._subscribers.clear()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""B63. Evidence Graph for Provenance.
|
|
2
|
+
|
|
3
|
+
Link every claim to source text snippets with traceable citations.
|
|
4
|
+
Source: Deep-Research-Agent pattern.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class Source:
|
|
13
|
+
id: str
|
|
14
|
+
title: str
|
|
15
|
+
url: str = ""
|
|
16
|
+
content: str = ""
|
|
17
|
+
retrieved_at: str = ""
|
|
18
|
+
trust_score: float = 0.5
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Claim:
|
|
23
|
+
id: str
|
|
24
|
+
text: str
|
|
25
|
+
source_ids: list[str] = field(default_factory=list)
|
|
26
|
+
evidence_spans: list[dict] = field(default_factory=list)
|
|
27
|
+
confidence: float = 0.0
|
|
28
|
+
contradictions: list[str] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class EvidenceEdge:
|
|
33
|
+
claim_id: str
|
|
34
|
+
source_id: str
|
|
35
|
+
span_start: int = 0
|
|
36
|
+
span_end: int = 0
|
|
37
|
+
relevance: float = 0.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EvidenceGraph:
|
|
41
|
+
"""Graph linking claims to source evidence for provenance."""
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
self._sources: dict[str, Source] = {}
|
|
45
|
+
self._claims: dict[str, Claim] = {}
|
|
46
|
+
self._edges: list[EvidenceEdge] = []
|
|
47
|
+
|
|
48
|
+
def add_source(self, source: Source) -> None:
|
|
49
|
+
self._sources[source.id] = source
|
|
50
|
+
|
|
51
|
+
def add_claim(self, claim: Claim) -> None:
|
|
52
|
+
self._claims[claim.id] = claim
|
|
53
|
+
for sid in claim.source_ids:
|
|
54
|
+
if sid in self._sources:
|
|
55
|
+
self._edges.append(EvidenceEdge(claim_id=claim.id, source_id=sid))
|
|
56
|
+
|
|
57
|
+
def link_evidence(self, claim_id: str, source_id: str,
|
|
58
|
+
span_start: int = 0, span_end: int = 0,
|
|
59
|
+
relevance: float = 1.0) -> None:
|
|
60
|
+
self._edges.append(EvidenceEdge(
|
|
61
|
+
claim_id=claim_id, source_id=source_id,
|
|
62
|
+
span_start=span_start, span_end=span_end, relevance=relevance,
|
|
63
|
+
))
|
|
64
|
+
claim = self._claims.get(claim_id)
|
|
65
|
+
if claim and source_id not in claim.source_ids:
|
|
66
|
+
claim.source_ids.append(source_id)
|
|
67
|
+
|
|
68
|
+
def get_evidence(self, claim_id: str) -> list[tuple[Source, EvidenceEdge]]:
|
|
69
|
+
"""Get all source evidence for a claim."""
|
|
70
|
+
result: list[tuple[Source, EvidenceEdge]] = []
|
|
71
|
+
for edge in self._edges:
|
|
72
|
+
if edge.claim_id == claim_id:
|
|
73
|
+
source = self._sources.get(edge.source_id)
|
|
74
|
+
if source:
|
|
75
|
+
result.append((source, edge))
|
|
76
|
+
return sorted(result, key=lambda x: x[1].relevance, reverse=True)
|
|
77
|
+
|
|
78
|
+
def find_contradictions(self) -> list[tuple[str, str, str]]:
|
|
79
|
+
"""Find claims that contradict each other."""
|
|
80
|
+
contradictions: list[tuple[str, str, str]] = []
|
|
81
|
+
claim_list = list(self._claims.values())
|
|
82
|
+
for i, a in enumerate(claim_list):
|
|
83
|
+
for b in claim_list[i + 1:]:
|
|
84
|
+
if a.source_ids and b.source_ids:
|
|
85
|
+
shared = set(a.source_ids) & set(b.source_ids)
|
|
86
|
+
if shared and a.text != b.text:
|
|
87
|
+
# Simple heuristic: if same source but different claims
|
|
88
|
+
contradictions.append((a.id, b.id, "same source, different claims"))
|
|
89
|
+
return contradictions
|
|
90
|
+
|
|
91
|
+
def render_citations(self, claim_id: str) -> str:
|
|
92
|
+
"""Render citations for a claim in [1], [2] format."""
|
|
93
|
+
evidence = self.get_evidence(claim_id)
|
|
94
|
+
if not evidence:
|
|
95
|
+
return ""
|
|
96
|
+
citations: list[str] = []
|
|
97
|
+
for i, (source, edge) in enumerate(evidence, 1):
|
|
98
|
+
span_text = ""
|
|
99
|
+
if edge.span_end > edge.span_start:
|
|
100
|
+
span_text = source.content[edge.span_start:edge.span_end]
|
|
101
|
+
citations.append(f"[{i}] {source.title} — {span_text[:100]}")
|
|
102
|
+
return "\n".join(citations)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def source_count(self) -> int:
|
|
106
|
+
return len(self._sources)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def claim_count(self) -> int:
|
|
110
|
+
return len(self._claims)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def edge_count(self) -> int:
|
|
114
|
+
return len(self._edges)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""B38. Extension Host Isolation + Declarative Contribution Points (VS Code Pattern).
|
|
2
|
+
|
|
3
|
+
Extensions declare what they contribute (commands, tools, converters,
|
|
4
|
+
maintenance checks) via a manifest. Activation events control when an
|
|
5
|
+
extension's code is loaded. Extensions run behind a boundary and cannot
|
|
6
|
+
directly mutate files or send externally.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any, Callable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ── contribution schema ───────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class CommandContribution:
|
|
20
|
+
id: str
|
|
21
|
+
title: str
|
|
22
|
+
handler: Callable[[], str] | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class ToolContribution:
|
|
27
|
+
id: str
|
|
28
|
+
description: str
|
|
29
|
+
handler: Callable[[dict], str] | None = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ConverterContribution:
|
|
34
|
+
id: str
|
|
35
|
+
suffixes: list[str]
|
|
36
|
+
handler: Callable[[str], str] | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class MaintenanceCheckContribution:
|
|
41
|
+
id: str
|
|
42
|
+
name: str
|
|
43
|
+
handler: Callable[[], dict] | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ExtensionManifest:
|
|
48
|
+
"""Declarative manifest for an Algo CLI extension."""
|
|
49
|
+
id: str
|
|
50
|
+
name: str
|
|
51
|
+
version: str = "0.1.0"
|
|
52
|
+
enabled: bool = True
|
|
53
|
+
activation_events: list[str] = field(default_factory=list)
|
|
54
|
+
contributes_commands: list[CommandContribution] = field(default_factory=list)
|
|
55
|
+
contributes_tools: list[ToolContribution] = field(default_factory=list)
|
|
56
|
+
contributes_converters: list[ConverterContribution] = field(default_factory=list)
|
|
57
|
+
contributes_maintenance_checks: list[MaintenanceCheckContribution] = field(default_factory=list)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ── activation events ─────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def matches_activation(event: str, context: dict[str, Any]) -> bool:
|
|
64
|
+
"""Check if an activation event matches the current context."""
|
|
65
|
+
if event == "*":
|
|
66
|
+
return True
|
|
67
|
+
if event.startswith("onCommand:"):
|
|
68
|
+
return context.get("command") == event.split(":", 1)[1]
|
|
69
|
+
if event.startswith("onFileType:"):
|
|
70
|
+
return context.get("file_suffix") == event.split(":", 1)[1]
|
|
71
|
+
if event.startswith("onProject:"):
|
|
72
|
+
return context.get("project") == event.split(":", 1)[1]
|
|
73
|
+
if event.startswith("onSchedule:"):
|
|
74
|
+
return context.get("schedule") == event.split(":", 1)[1]
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ── extension host ────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ExtensionHost:
|
|
82
|
+
"""Manages extensions with activation and contribution resolution."""
|
|
83
|
+
|
|
84
|
+
def __init__(self) -> None:
|
|
85
|
+
self._manifests: dict[str, ExtensionManifest] = {}
|
|
86
|
+
self._activated: set[str] = set()
|
|
87
|
+
|
|
88
|
+
def register(self, manifest: ExtensionManifest) -> None:
|
|
89
|
+
self._manifests[manifest.id] = manifest
|
|
90
|
+
|
|
91
|
+
def activate(self, ext_id: str, context: dict[str, Any] | None = None) -> bool:
|
|
92
|
+
"""Activate an extension if its activation events match."""
|
|
93
|
+
manifest = self._manifests.get(ext_id)
|
|
94
|
+
if not manifest or not manifest.enabled:
|
|
95
|
+
return False
|
|
96
|
+
ctx = context or {}
|
|
97
|
+
if any(matches_activation(ev, ctx) for ev in manifest.activation_events):
|
|
98
|
+
self._activated.add(ext_id)
|
|
99
|
+
return True
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def is_activated(self, ext_id: str) -> bool:
|
|
103
|
+
return ext_id in self._activated
|
|
104
|
+
|
|
105
|
+
def disable(self, ext_id: str) -> None:
|
|
106
|
+
manifest = self._manifests.get(ext_id)
|
|
107
|
+
if manifest:
|
|
108
|
+
manifest.enabled = False
|
|
109
|
+
self._activated.discard(ext_id)
|
|
110
|
+
|
|
111
|
+
def all_commands(self) -> list[CommandContribution]:
|
|
112
|
+
"""Get commands from all activated extensions."""
|
|
113
|
+
commands: list[CommandContribution] = []
|
|
114
|
+
for ext_id in self._activated:
|
|
115
|
+
manifest = self._manifests[ext_id]
|
|
116
|
+
commands.extend(manifest.contributes_commands)
|
|
117
|
+
return commands
|
|
118
|
+
|
|
119
|
+
def all_tools(self) -> list[ToolContribution]:
|
|
120
|
+
tools: list[ToolContribution] = []
|
|
121
|
+
for ext_id in self._activated:
|
|
122
|
+
manifest = self._manifests[ext_id]
|
|
123
|
+
tools.extend(manifest.contributes_tools)
|
|
124
|
+
return tools
|
|
125
|
+
|
|
126
|
+
def all_converters(self) -> list[ConverterContribution]:
|
|
127
|
+
converters: list[ConverterContribution] = []
|
|
128
|
+
for ext_id in self._activated:
|
|
129
|
+
manifest = self._manifests[ext_id]
|
|
130
|
+
converters.extend(manifest.contributes_converters)
|
|
131
|
+
return converters
|
|
132
|
+
|
|
133
|
+
def all_maintenance_checks(self) -> list[MaintenanceCheckContribution]:
|
|
134
|
+
checks: list[MaintenanceCheckContribution] = []
|
|
135
|
+
for ext_id in self._activated:
|
|
136
|
+
manifest = self._manifests[ext_id]
|
|
137
|
+
checks.extend(manifest.contributes_maintenance_checks)
|
|
138
|
+
return checks
|
|
139
|
+
|
|
140
|
+
def execute_command(self, command_id: str) -> str:
|
|
141
|
+
"""Execute a contributed command (gated by activation)."""
|
|
142
|
+
for ext_id in self._activated:
|
|
143
|
+
manifest = self._manifests[ext_id]
|
|
144
|
+
for cmd in manifest.contributes_commands:
|
|
145
|
+
if cmd.id == command_id and cmd.handler:
|
|
146
|
+
return cmd.handler()
|
|
147
|
+
return f"Command {command_id} not found or not activated"
|
|
148
|
+
|
|
149
|
+
def validate_manifest(self, ext_id: str) -> list[str]:
|
|
150
|
+
"""Validate a manifest and return list of issues."""
|
|
151
|
+
manifest = self._manifests.get(ext_id)
|
|
152
|
+
if not manifest:
|
|
153
|
+
return ["manifest not found"]
|
|
154
|
+
issues: list[str] = []
|
|
155
|
+
if not manifest.id:
|
|
156
|
+
issues.append("missing id")
|
|
157
|
+
if not manifest.name:
|
|
158
|
+
issues.append("missing name")
|
|
159
|
+
for cmd in manifest.contributes_commands:
|
|
160
|
+
if not cmd.id:
|
|
161
|
+
issues.append("command missing id")
|
|
162
|
+
return issues
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""B57. Extension Manifest Schema + Catalog.
|
|
2
|
+
|
|
3
|
+
YAML manifest with commands/config/hooks. Extension registry with
|
|
4
|
+
multi-source catalog and priority. Source: spec-kit pattern.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum, auto
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExtensionStatus(Enum):
|
|
14
|
+
DISCOVERED = auto()
|
|
15
|
+
INSTALLED = auto()
|
|
16
|
+
ENABLED = auto()
|
|
17
|
+
DISABLED = auto()
|
|
18
|
+
ERROR = auto()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ExtensionContribution:
|
|
23
|
+
type: str # "command", "tool", "hook", "contextProvider", "fileConverter"
|
|
24
|
+
id: str
|
|
25
|
+
handler: str = "" # module:function or path
|
|
26
|
+
config: dict[str, Any] = field(default_factory=dict)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class ExtensionManifest:
|
|
31
|
+
id: str
|
|
32
|
+
name: str
|
|
33
|
+
version: str
|
|
34
|
+
description: str = ""
|
|
35
|
+
author: str = ""
|
|
36
|
+
priority: int = 100 # lower = higher priority
|
|
37
|
+
activation_events: list[str] = field(default_factory=list)
|
|
38
|
+
contributes: list[ExtensionContribution] = field(default_factory=list)
|
|
39
|
+
config_schema: dict[str, Any] = field(default_factory=dict)
|
|
40
|
+
status: ExtensionStatus = ExtensionStatus.DISCOVERED
|
|
41
|
+
|
|
42
|
+
def to_dict(self) -> dict:
|
|
43
|
+
return {
|
|
44
|
+
"id": self.id,
|
|
45
|
+
"name": self.name,
|
|
46
|
+
"version": self.version,
|
|
47
|
+
"description": self.description,
|
|
48
|
+
"author": self.author,
|
|
49
|
+
"priority": self.priority,
|
|
50
|
+
"activation_events": self.activation_events,
|
|
51
|
+
"contributes": [
|
|
52
|
+
{"type": c.type, "id": c.id, "handler": c.handler, "config": c.config}
|
|
53
|
+
for c in self.contributes
|
|
54
|
+
],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class CatalogSource:
|
|
60
|
+
name: str
|
|
61
|
+
url: str = ""
|
|
62
|
+
trust_level: str = "local" # local, trusted, community
|
|
63
|
+
priority: int = 100
|
|
64
|
+
enabled: bool = True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ExtensionCatalog:
|
|
68
|
+
"""Multi-source extension catalog with priority-based resolution."""
|
|
69
|
+
|
|
70
|
+
def __init__(self) -> None:
|
|
71
|
+
self._sources: dict[str, CatalogSource] = {}
|
|
72
|
+
self._extensions: dict[str, ExtensionManifest] = {}
|
|
73
|
+
|
|
74
|
+
def add_source(self, source: CatalogSource) -> None:
|
|
75
|
+
self._sources[source.name] = source
|
|
76
|
+
|
|
77
|
+
def register(self, manifest: ExtensionManifest, source_name: str = "local") -> None:
|
|
78
|
+
self._extensions[manifest.id] = manifest
|
|
79
|
+
|
|
80
|
+
def get(self, ext_id: str) -> ExtensionManifest | None:
|
|
81
|
+
return self._extensions.get(ext_id)
|
|
82
|
+
|
|
83
|
+
def list_extensions(self, status: ExtensionStatus | None = None) -> list[ExtensionManifest]:
|
|
84
|
+
exts = list(self._extensions.values())
|
|
85
|
+
if status:
|
|
86
|
+
exts = [e for e in exts if e.status == status]
|
|
87
|
+
return sorted(exts, key=lambda e: e.priority)
|
|
88
|
+
|
|
89
|
+
def find_contribution(self, contrib_type: str, contrib_id: str) -> ExtensionManifest | None:
|
|
90
|
+
for ext in self._extensions.values():
|
|
91
|
+
for c in ext.contributes:
|
|
92
|
+
if c.type == contrib_type and c.id == contrib_id:
|
|
93
|
+
return ext
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
def enable(self, ext_id: str) -> bool:
|
|
97
|
+
ext = self._extensions.get(ext_id)
|
|
98
|
+
if ext:
|
|
99
|
+
ext.status = ExtensionStatus.ENABLED
|
|
100
|
+
return True
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def disable(self, ext_id: str) -> bool:
|
|
104
|
+
ext = self._extensions.get(ext_id)
|
|
105
|
+
if ext:
|
|
106
|
+
ext.status = ExtensionStatus.DISABLED
|
|
107
|
+
return True
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
def match_activation(self, event: str) -> list[ExtensionManifest]:
|
|
111
|
+
"""Find extensions whose activation events match."""
|
|
112
|
+
return [
|
|
113
|
+
e for e in self._extensions.values()
|
|
114
|
+
if e.status == ExtensionStatus.ENABLED and event in e.activation_events
|
|
115
|
+
]
|