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,118 @@
|
|
|
1
|
+
"""B75. Cavecrew: Model Specialization per Role.
|
|
2
|
+
|
|
3
|
+
Match model cost to task complexity.
|
|
4
|
+
Investigator (cheap) → Builder (standard) → Reviewer (cheap) → Orchestrator (expensive).
|
|
5
|
+
Saves ~60% tokens. Source: Claudient 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 CrewRole(Enum):
|
|
15
|
+
INVESTIGATOR = auto() # cheap model — search, read, gather
|
|
16
|
+
BUILDER = auto() # standard model — write, edit, implement
|
|
17
|
+
REVIEWER = auto() # cheap model — check, verify, audit
|
|
18
|
+
ORCHESTRATOR = auto() # expensive model — plan, decide, coordinate
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class CrewMember:
|
|
23
|
+
role: CrewRole
|
|
24
|
+
model: str
|
|
25
|
+
system_prompt: str = ""
|
|
26
|
+
max_iterations: int = 5
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CrewTask:
|
|
31
|
+
description: str
|
|
32
|
+
assigned_role: CrewRole
|
|
33
|
+
dependencies: list[str] = field(default_factory=list)
|
|
34
|
+
result: str = ""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
DEFAULT_CREW: dict[CrewRole, CrewMember] = {
|
|
38
|
+
CrewRole.INVESTIGATOR: CrewMember(
|
|
39
|
+
role=CrewRole.INVESTIGATOR,
|
|
40
|
+
model="qwen3:4b",
|
|
41
|
+
system_prompt="You are an investigator. Search, read, and gather information. Be concise.",
|
|
42
|
+
max_iterations=3,
|
|
43
|
+
),
|
|
44
|
+
CrewRole.BUILDER: CrewMember(
|
|
45
|
+
role=CrewRole.BUILDER,
|
|
46
|
+
model="glm-5.2",
|
|
47
|
+
system_prompt="You are a builder. Write and edit code. Follow existing patterns.",
|
|
48
|
+
max_iterations=5,
|
|
49
|
+
),
|
|
50
|
+
CrewRole.REVIEWER: CrewMember(
|
|
51
|
+
role=CrewRole.REVIEWER,
|
|
52
|
+
model="qwen3:4b",
|
|
53
|
+
system_prompt="You are a reviewer. Check for errors, missing tests, and improvements.",
|
|
54
|
+
max_iterations=3,
|
|
55
|
+
),
|
|
56
|
+
CrewRole.ORCHESTRATOR: CrewMember(
|
|
57
|
+
role=CrewRole.ORCHESTRATOR,
|
|
58
|
+
model="gpt-5.5",
|
|
59
|
+
system_prompt="You are the orchestrator. Plan, decide, and coordinate. Be strategic.",
|
|
60
|
+
max_iterations=5,
|
|
61
|
+
),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Cavecrew:
|
|
66
|
+
"""Model-specialized crew for cost-efficient multi-agent work."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, crew: dict[CrewRole, CrewMember] | None = None) -> None:
|
|
69
|
+
self._crew = crew or dict(DEFAULT_CREW)
|
|
70
|
+
self._tasks: list[CrewTask] = []
|
|
71
|
+
self._results: dict[str, str] = {}
|
|
72
|
+
|
|
73
|
+
def assign(self, task: CrewTask) -> None:
|
|
74
|
+
self._tasks.append(task)
|
|
75
|
+
|
|
76
|
+
def run(self, run_fn: Callable[[CrewMember, str, str], str]) -> dict[str, str]:
|
|
77
|
+
"""Execute tasks in dependency order, routing to appropriate models."""
|
|
78
|
+
completed: set[str] = set()
|
|
79
|
+
|
|
80
|
+
for _ in range(len(self._tasks) * 2): # safety limit
|
|
81
|
+
ready = [
|
|
82
|
+
t for t in self._tasks
|
|
83
|
+
if t.description not in completed
|
|
84
|
+
and all(d in completed for d in t.dependencies)
|
|
85
|
+
]
|
|
86
|
+
if not ready:
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
for task in ready:
|
|
90
|
+
member = self._crew.get(task.assigned_role)
|
|
91
|
+
if not member:
|
|
92
|
+
continue
|
|
93
|
+
try:
|
|
94
|
+
result = run_fn(member, member.system_prompt, task.description)
|
|
95
|
+
task.result = result
|
|
96
|
+
self._results[task.description] = result
|
|
97
|
+
completed.add(task.description)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
task.result = f"ERROR: {e}"
|
|
100
|
+
self._results[task.description] = task.result
|
|
101
|
+
completed.add(task.description)
|
|
102
|
+
|
|
103
|
+
return dict(self._results)
|
|
104
|
+
|
|
105
|
+
def estimate_cost(self) -> dict[CrewRole, int]:
|
|
106
|
+
"""Estimate relative token cost per role."""
|
|
107
|
+
costs = {
|
|
108
|
+
CrewRole.INVESTIGATOR: 1, # cheap
|
|
109
|
+
CrewRole.BUILDER: 3, # standard
|
|
110
|
+
CrewRole.REVIEWER: 1, # cheap
|
|
111
|
+
CrewRole.ORCHESTRATOR: 5, # expensive
|
|
112
|
+
}
|
|
113
|
+
return {role: costs.get(role, 1) * sum(1 for t in self._tasks if t.assigned_role == role)
|
|
114
|
+
for role in CrewRole}
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def crew(self) -> dict[CrewRole, CrewMember]:
|
|
118
|
+
return dict(self._crew)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""B44. Changefile-Driven Release Notes (Microsoft Beachball Pattern).
|
|
2
|
+
|
|
3
|
+
Requires a changefile for every user-visible code change. Generates
|
|
4
|
+
release notes grouped by kind (feature, fix, maintenance, safety, docs,
|
|
5
|
+
breaking). Validates that no release is missing changefiles.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ChangeKind:
|
|
18
|
+
FEATURE = "feature"
|
|
19
|
+
FIX = "fix"
|
|
20
|
+
MAINTENANCE = "maintenance"
|
|
21
|
+
SAFETY = "safety"
|
|
22
|
+
DOCS = "docs"
|
|
23
|
+
BREAKING = "breaking"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ChangeFile:
|
|
28
|
+
"""A single change file (changes/*.json)."""
|
|
29
|
+
filename: str
|
|
30
|
+
kind: str
|
|
31
|
+
description: str
|
|
32
|
+
component: str = ""
|
|
33
|
+
author: str = ""
|
|
34
|
+
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
35
|
+
version_bump: str = "patch" # "patch", "minor", "major"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ReleaseNotes:
|
|
40
|
+
version: str
|
|
41
|
+
date: str
|
|
42
|
+
sections: dict[str, list[str]] = field(default_factory=dict)
|
|
43
|
+
breaking_changes: list[str] = field(default_factory=list)
|
|
44
|
+
total_changes: int = 0
|
|
45
|
+
|
|
46
|
+
def to_markdown(self) -> str:
|
|
47
|
+
lines = [f"# Release {self.version}", f"Date: {self.date}", ""]
|
|
48
|
+
if self.breaking_changes:
|
|
49
|
+
lines.append("## ⚠️ Breaking Changes")
|
|
50
|
+
for bc in self.breaking_changes:
|
|
51
|
+
lines.append(f"- {bc}")
|
|
52
|
+
lines.append("")
|
|
53
|
+
kind_order = [ChangeKind.FEATURE, ChangeKind.FIX, ChangeKind.SAFETY, ChangeKind.MAINTENANCE, ChangeKind.DOCS]
|
|
54
|
+
kind_labels = {
|
|
55
|
+
ChangeKind.FEATURE: "✨ Features",
|
|
56
|
+
ChangeKind.FIX: "🐛 Fixes",
|
|
57
|
+
ChangeKind.SAFETY: "🛡️ Safety",
|
|
58
|
+
ChangeKind.MAINTENANCE: "🔧 Maintenance",
|
|
59
|
+
ChangeKind.DOCS: "📚 Documentation",
|
|
60
|
+
}
|
|
61
|
+
for kind in kind_order:
|
|
62
|
+
entries = self.sections.get(kind, [])
|
|
63
|
+
if entries:
|
|
64
|
+
lines.append(f"## {kind_labels.get(kind, kind)}")
|
|
65
|
+
for entry in entries:
|
|
66
|
+
lines.append(f"- {entry}")
|
|
67
|
+
lines.append("")
|
|
68
|
+
return "\n".join(lines)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ChangelogManager:
|
|
72
|
+
"""Manages change files and release note generation."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, changes_dir: str = "changes"):
|
|
75
|
+
self.changes_dir = Path(changes_dir)
|
|
76
|
+
self._changes: list[ChangeFile] = []
|
|
77
|
+
|
|
78
|
+
def add_change(self, change: ChangeFile) -> None:
|
|
79
|
+
self._changes.append(change)
|
|
80
|
+
|
|
81
|
+
def load_from_dir(self, directory: Path | None = None) -> list[ChangeFile]:
|
|
82
|
+
"""Load change files from a directory."""
|
|
83
|
+
d = directory or self.changes_dir
|
|
84
|
+
if not d.exists():
|
|
85
|
+
return []
|
|
86
|
+
loaded: list[ChangeFile] = []
|
|
87
|
+
for f in sorted(d.glob("*.json")):
|
|
88
|
+
try:
|
|
89
|
+
data = json.loads(f.read_text(encoding="utf-8"))
|
|
90
|
+
loaded.append(ChangeFile(
|
|
91
|
+
filename=f.name,
|
|
92
|
+
kind=data.get("kind", ChangeKind.MAINTENANCE),
|
|
93
|
+
description=data.get("description", ""),
|
|
94
|
+
component=data.get("component", ""),
|
|
95
|
+
author=data.get("author", ""),
|
|
96
|
+
created_at=data.get("created_at", datetime.now().isoformat()),
|
|
97
|
+
version_bump=data.get("version_bump", "patch"),
|
|
98
|
+
))
|
|
99
|
+
except (json.JSONDecodeError, KeyError):
|
|
100
|
+
continue
|
|
101
|
+
self._changes.extend(loaded)
|
|
102
|
+
return loaded
|
|
103
|
+
|
|
104
|
+
def pending_changes(self) -> list[ChangeFile]:
|
|
105
|
+
"""Return all loaded changes (not yet released)."""
|
|
106
|
+
return list(self._changes)
|
|
107
|
+
|
|
108
|
+
def has_changes(self) -> bool:
|
|
109
|
+
return len(self._changes) > 0
|
|
110
|
+
|
|
111
|
+
def required_version_bump(self) -> str:
|
|
112
|
+
"""Determine the minimum version bump from pending changes."""
|
|
113
|
+
if any(c.version_bump == "major" or c.kind == ChangeKind.BREAKING for c in self._changes):
|
|
114
|
+
return "major"
|
|
115
|
+
if any(c.version_bump == "minor" or c.kind == ChangeKind.FEATURE for c in self._changes):
|
|
116
|
+
return "minor"
|
|
117
|
+
return "patch"
|
|
118
|
+
|
|
119
|
+
def generate_release_notes(self, version: str, date: str | None = None) -> ReleaseNotes:
|
|
120
|
+
"""Generate release notes from pending changes."""
|
|
121
|
+
date = date or datetime.now().strftime("%Y-%m-%d")
|
|
122
|
+
sections: dict[str, list[str]] = {}
|
|
123
|
+
breaking: list[str] = []
|
|
124
|
+
for change in self._changes:
|
|
125
|
+
kind = change.kind
|
|
126
|
+
if kind == ChangeKind.BREAKING:
|
|
127
|
+
breaking.append(change.description)
|
|
128
|
+
kind = ChangeKind.FIX # also list under fixes
|
|
129
|
+
sections.setdefault(kind, []).append(
|
|
130
|
+
f"{change.description}" + (f" ({change.component})" if change.component else "")
|
|
131
|
+
)
|
|
132
|
+
notes = ReleaseNotes(
|
|
133
|
+
version=version,
|
|
134
|
+
date=date,
|
|
135
|
+
sections=sections,
|
|
136
|
+
breaking_changes=breaking,
|
|
137
|
+
total_changes=len(self._changes),
|
|
138
|
+
)
|
|
139
|
+
return notes
|
|
140
|
+
|
|
141
|
+
def release_check(self) -> dict[str, Any]:
|
|
142
|
+
"""Validate release readiness."""
|
|
143
|
+
issues: list[str] = []
|
|
144
|
+
if not self._changes:
|
|
145
|
+
issues.append("no changefiles found — user-visible changes require a changefile")
|
|
146
|
+
for c in self._changes:
|
|
147
|
+
if not c.description:
|
|
148
|
+
issues.append(f"changefile {c.filename} has empty description")
|
|
149
|
+
if c.kind not in [ChangeKind.FEATURE, ChangeKind.FIX, ChangeKind.MAINTENANCE, ChangeKind.SAFETY, ChangeKind.DOCS, ChangeKind.BREAKING]:
|
|
150
|
+
issues.append(f"changefile {c.filename} has unknown kind: {c.kind}")
|
|
151
|
+
return {
|
|
152
|
+
"ready": len(issues) == 0,
|
|
153
|
+
"issues": issues,
|
|
154
|
+
"change_count": len(self._changes),
|
|
155
|
+
"required_bump": self.required_version_bump(),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
def clear_released(self) -> None:
|
|
159
|
+
"""Clear changes after generating release notes."""
|
|
160
|
+
self._changes.clear()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def write_change_file(directory: Path, name: str, kind: str, description: str, component: str = "", author: str = "") -> Path:
|
|
164
|
+
"""Write a change file to disk."""
|
|
165
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
data = {
|
|
167
|
+
"kind": kind,
|
|
168
|
+
"description": description,
|
|
169
|
+
"component": component,
|
|
170
|
+
"author": author,
|
|
171
|
+
"created_at": datetime.now().isoformat(),
|
|
172
|
+
"version_bump": "major" if kind == ChangeKind.BREAKING else "minor" if kind == ChangeKind.FEATURE else "patch",
|
|
173
|
+
}
|
|
174
|
+
path = directory / f"{name}.json"
|
|
175
|
+
path.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
176
|
+
return path
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""H11 — Checkpoint/Resume.
|
|
2
|
+
|
|
3
|
+
Resumable long catalog operations via state serialization.
|
|
4
|
+
Mined from GLOSSOPETRAE experiment harnesses.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Checkpoint:
|
|
16
|
+
"""A single checkpoint capturing operation state."""
|
|
17
|
+
|
|
18
|
+
operation_id: str
|
|
19
|
+
step: int
|
|
20
|
+
total_steps: int
|
|
21
|
+
state: dict[str, Any] = field(default_factory=dict)
|
|
22
|
+
created_at: float = field(default_factory=time.time)
|
|
23
|
+
completed: bool = False
|
|
24
|
+
|
|
25
|
+
def to_dict(self) -> dict[str, Any]:
|
|
26
|
+
return {
|
|
27
|
+
"operation_id": self.operation_id,
|
|
28
|
+
"step": self.step,
|
|
29
|
+
"total_steps": self.total_steps,
|
|
30
|
+
"state": dict(self.state),
|
|
31
|
+
"created_at": self.created_at,
|
|
32
|
+
"completed": self.completed,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_dict(cls, data: dict[str, Any]) -> Checkpoint:
|
|
37
|
+
return cls(
|
|
38
|
+
operation_id=data["operation_id"],
|
|
39
|
+
step=data["step"],
|
|
40
|
+
total_steps=data["total_steps"],
|
|
41
|
+
state=dict(data.get("state", {})),
|
|
42
|
+
created_at=data.get("created_at", time.time()),
|
|
43
|
+
completed=data.get("completed", False),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CheckpointManager:
|
|
48
|
+
"""Save and load checkpoints for resumable operations."""
|
|
49
|
+
|
|
50
|
+
def __init__(self) -> None:
|
|
51
|
+
self._checkpoints: dict[str, Checkpoint] = {}
|
|
52
|
+
|
|
53
|
+
def save(self, checkpoint: Checkpoint) -> None:
|
|
54
|
+
self._checkpoints[checkpoint.operation_id] = checkpoint
|
|
55
|
+
|
|
56
|
+
def load(self, operation_id: str) -> Checkpoint | None:
|
|
57
|
+
return self._checkpoints.get(operation_id)
|
|
58
|
+
|
|
59
|
+
def complete(self, operation_id: str) -> Checkpoint | None:
|
|
60
|
+
cp = self._checkpoints.get(operation_id)
|
|
61
|
+
if cp is not None:
|
|
62
|
+
cp.completed = True
|
|
63
|
+
return cp
|
|
64
|
+
|
|
65
|
+
def is_complete(self, operation_id: str) -> bool:
|
|
66
|
+
cp = self._checkpoints.get(operation_id)
|
|
67
|
+
return cp is not None and cp.completed
|
|
68
|
+
|
|
69
|
+
def resume_step(self, operation_id: str) -> int:
|
|
70
|
+
"""Return the step to resume from (0 if no checkpoint)."""
|
|
71
|
+
cp = self._checkpoints.get(operation_id)
|
|
72
|
+
if cp is None or cp.completed:
|
|
73
|
+
return 0
|
|
74
|
+
return cp.step
|
|
75
|
+
|
|
76
|
+
def serialize(self, operation_id: str) -> str:
|
|
77
|
+
cp = self._checkpoints.get(operation_id)
|
|
78
|
+
if cp is None:
|
|
79
|
+
raise KeyError(f"No checkpoint for {operation_id!r}")
|
|
80
|
+
return json.dumps(cp.to_dict())
|
|
81
|
+
|
|
82
|
+
def deserialize(self, data: str) -> Checkpoint:
|
|
83
|
+
return Checkpoint.from_dict(json.loads(data))
|
|
84
|
+
|
|
85
|
+
def all(self) -> list[Checkpoint]:
|
|
86
|
+
return list(self._checkpoints.values())
|
|
87
|
+
|
|
88
|
+
def count(self) -> int:
|
|
89
|
+
return len(self._checkpoints)
|
|
90
|
+
|
|
91
|
+
def clear(self) -> None:
|
|
92
|
+
self._checkpoints.clear()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""H16 — Detection Risk Circuit Breaker.
|
|
2
|
+
|
|
3
|
+
Accumulate risk per failure, auto-disable at threshold.
|
|
4
|
+
Mined from T3MP3ST WHITEPAPER §3.5.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Callable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class CircuitState:
|
|
14
|
+
"""State for a single circuit."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
risk: float = 0.0
|
|
18
|
+
threshold: float = 1.0
|
|
19
|
+
increment: float = 0.1
|
|
20
|
+
tripped: bool = False
|
|
21
|
+
trip_count: int = 0
|
|
22
|
+
last_failure: str = ""
|
|
23
|
+
metadata: dict[str, object] = field(default_factory=dict)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CircuitBreaker:
|
|
27
|
+
"""Manage multiple named circuit breakers."""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self._circuits: dict[str, CircuitState] = {}
|
|
31
|
+
self._on_trip: list[Callable[[str, CircuitState], None]] = []
|
|
32
|
+
|
|
33
|
+
def register(
|
|
34
|
+
self,
|
|
35
|
+
name: str,
|
|
36
|
+
threshold: float = 1.0,
|
|
37
|
+
increment: float = 0.1,
|
|
38
|
+
) -> CircuitState:
|
|
39
|
+
circuit = CircuitState(name=name, threshold=threshold, increment=increment)
|
|
40
|
+
self._circuits[name] = circuit
|
|
41
|
+
return circuit
|
|
42
|
+
|
|
43
|
+
def record_failure(self, name: str, reason: str = "") -> CircuitState:
|
|
44
|
+
circuit = self._circuits.get(name)
|
|
45
|
+
if circuit is None:
|
|
46
|
+
raise KeyError(f"Circuit {name!r} not registered")
|
|
47
|
+
if circuit.tripped:
|
|
48
|
+
return circuit
|
|
49
|
+
circuit.risk += circuit.increment
|
|
50
|
+
circuit.last_failure = reason
|
|
51
|
+
if circuit.risk >= circuit.threshold:
|
|
52
|
+
circuit.tripped = True
|
|
53
|
+
circuit.trip_count += 1
|
|
54
|
+
for cb in self._on_trip:
|
|
55
|
+
cb(name, circuit)
|
|
56
|
+
return circuit
|
|
57
|
+
|
|
58
|
+
def record_success(self, name: str) -> CircuitState:
|
|
59
|
+
circuit = self._circuits.get(name)
|
|
60
|
+
if circuit is None:
|
|
61
|
+
raise KeyError(f"Circuit {name!r} not registered")
|
|
62
|
+
circuit.risk = max(0.0, circuit.risk - circuit.increment)
|
|
63
|
+
return circuit
|
|
64
|
+
|
|
65
|
+
def is_tripped(self, name: str) -> bool:
|
|
66
|
+
circuit = self._circuits.get(name)
|
|
67
|
+
return circuit is not None and circuit.tripped
|
|
68
|
+
|
|
69
|
+
def reset(self, name: str) -> CircuitState:
|
|
70
|
+
circuit = self._circuits.get(name)
|
|
71
|
+
if circuit is None:
|
|
72
|
+
raise KeyError(f"Circuit {name!r} not registered")
|
|
73
|
+
circuit.risk = 0.0
|
|
74
|
+
circuit.tripped = False
|
|
75
|
+
circuit.last_failure = ""
|
|
76
|
+
return circuit
|
|
77
|
+
|
|
78
|
+
def on_trip(self, callback: Callable[[str, CircuitState], None]) -> None:
|
|
79
|
+
self._on_trip.append(callback)
|
|
80
|
+
|
|
81
|
+
def get(self, name: str) -> CircuitState | None:
|
|
82
|
+
return self._circuits.get(name)
|
|
83
|
+
|
|
84
|
+
def all(self) -> list[CircuitState]:
|
|
85
|
+
return list(self._circuits.values())
|
|
86
|
+
|
|
87
|
+
def count(self) -> int:
|
|
88
|
+
return len(self._circuits)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""B67. Human-in-the-Loop Clarification.
|
|
2
|
+
|
|
3
|
+
Ask clarifying questions before expensive research.
|
|
4
|
+
Source: DeepResearch-Agent pattern.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum, auto
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ClarificationType(Enum):
|
|
13
|
+
SCOPE = auto() # "Do you mean X or Y?"
|
|
14
|
+
DEPTH = auto() # "Brief overview or deep dive?"
|
|
15
|
+
AUDIENCE = auto() # "For beginners or experts?"
|
|
16
|
+
FORMAT = auto() # "Report, bullet points, or code?"
|
|
17
|
+
TIMEFRAME = auto() # "Recent only or historical?"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Clarification:
|
|
22
|
+
id: str
|
|
23
|
+
type: ClarificationType
|
|
24
|
+
question: str
|
|
25
|
+
options: list[str] = field(default_factory=list)
|
|
26
|
+
default: str = ""
|
|
27
|
+
answer: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ClarificationResult:
|
|
32
|
+
clarifications: list[Clarification] = field(default_factory=list)
|
|
33
|
+
refined_query: str = ""
|
|
34
|
+
skipped: bool = False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ClarificationGate:
|
|
38
|
+
"""Generate and collect clarifying questions before research."""
|
|
39
|
+
|
|
40
|
+
AMBIGUOUS_WORDS = {"best", "good", "fast", "simple", "advanced", "modern", "latest"}
|
|
41
|
+
|
|
42
|
+
def generate(self, query: str) -> list[Clarification]:
|
|
43
|
+
"""Generate clarifying questions based on query analysis."""
|
|
44
|
+
clarifications: list[Clarification] = []
|
|
45
|
+
words = set(query.lower().split())
|
|
46
|
+
|
|
47
|
+
# Check for ambiguous terms
|
|
48
|
+
ambiguous = words & self.AMBIGUOUS_WORDS
|
|
49
|
+
if ambiguous:
|
|
50
|
+
clarifications.append(Clarification(
|
|
51
|
+
id="clar_1",
|
|
52
|
+
type=ClarificationType.SCOPE,
|
|
53
|
+
question=f"You mentioned '{', '.join(ambiguous)}'. Can you be more specific?",
|
|
54
|
+
default="general overview",
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
# Check for depth signals
|
|
58
|
+
if not any(w in query.lower() for w in ["deep", "detailed", "overview", "brief", "summary"]):
|
|
59
|
+
clarifications.append(Clarification(
|
|
60
|
+
id="clar_2",
|
|
61
|
+
type=ClarificationType.DEPTH,
|
|
62
|
+
question="How deep should the research go?",
|
|
63
|
+
options=["Brief overview", "Standard depth", "Deep dive"],
|
|
64
|
+
default="Standard depth",
|
|
65
|
+
))
|
|
66
|
+
|
|
67
|
+
# Check for format
|
|
68
|
+
if not any(w in query.lower() for w in ["report", "bullet", "code", "table", "list"]):
|
|
69
|
+
clarifications.append(Clarification(
|
|
70
|
+
id="clar_3",
|
|
71
|
+
type=ClarificationType.FORMAT,
|
|
72
|
+
question="What format would you prefer?",
|
|
73
|
+
options=["Markdown report", "Bullet points", "Code examples"],
|
|
74
|
+
default="Markdown report",
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
# Check for timeframe
|
|
78
|
+
if not any(w in query.lower() for w in ["2024", "2025", "2026", "recent", "latest", "historical"]):
|
|
79
|
+
clarifications.append(Clarification(
|
|
80
|
+
id="clar_4",
|
|
81
|
+
type=ClarificationType.TIMEFRAME,
|
|
82
|
+
question="What timeframe should I focus on?",
|
|
83
|
+
options=["Recent (2025-2026)", "Last 3 years", "Historical"],
|
|
84
|
+
default="Recent (2025-2026)",
|
|
85
|
+
))
|
|
86
|
+
|
|
87
|
+
return clarifications
|
|
88
|
+
|
|
89
|
+
def refine_query(self, query: str, answers: dict[str, str]) -> str:
|
|
90
|
+
"""Refine the original query based on clarification answers."""
|
|
91
|
+
refined = query
|
|
92
|
+
for clar_id, answer in answers.items():
|
|
93
|
+
if answer and answer != "general overview":
|
|
94
|
+
refined += f" ({answer})"
|
|
95
|
+
return refined
|
|
96
|
+
|
|
97
|
+
def should_ask(self, query: str, auto_skip: bool = False) -> bool:
|
|
98
|
+
"""Determine if clarification is needed."""
|
|
99
|
+
if auto_skip:
|
|
100
|
+
return False
|
|
101
|
+
return len(self.generate(query)) > 0
|