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
algo_cli/__init__.py
ADDED
algo_cli/__main__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""algo_cli/_internal — Internal helpers and runtime modules.
|
|
2
|
+
|
|
3
|
+
This subpackage is for code that the user does not import directly. It is
|
|
4
|
+
imported by algo_cli and its public API. Importing from this package
|
|
5
|
+
directly may emit a DeprecationWarning in a future version.
|
|
6
|
+
|
|
7
|
+
Modules:
|
|
8
|
+
- policy_chain: J10 — PAM-style policy chain (required/sufficient/requisite/include)
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
__all__: list[str] = []
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""PAM-style policy chain (J10).
|
|
2
|
+
|
|
3
|
+
A chain of named, composable checks with control flags
|
|
4
|
+
(required / sufficient / requisite / include), modeled on the
|
|
5
|
+
macOS PAM stack (/etc/pam.d/*).
|
|
6
|
+
|
|
7
|
+
Provenance: ALGO.md J10 (PAM-style policy chain), J5 (SHAuthorizationRight),
|
|
8
|
+
J13 (flat-text policy file). The chain is the upgrade path for
|
|
9
|
+
algo_cli/tool_policy.py from a single boolean to a composable,
|
|
10
|
+
audit-trail-preserving decision model.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from enum import Enum
|
|
16
|
+
from typing import Any, Callable, Sequence
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Control(str, Enum):
|
|
20
|
+
"""PAM-style control flags.
|
|
21
|
+
|
|
22
|
+
- REQUIRED: must pass; failure is recorded but the chain continues
|
|
23
|
+
to gather full evidence (PAM "required" semantics).
|
|
24
|
+
- SUFFICIENT: pass short-circuits the chain to PASS (PAM "sufficient").
|
|
25
|
+
- REQUISITE: must pass; failure aborts the chain immediately
|
|
26
|
+
(PAM "requisite" semantics — fail-fast).
|
|
27
|
+
- INCLUDE: recursively evaluate a named chain. The chain name is
|
|
28
|
+
read from the check's `name` field (PAM "include" semantics).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
REQUIRED = "required"
|
|
32
|
+
SUFFICIENT = "sufficient"
|
|
33
|
+
REQUISITE = "requisite"
|
|
34
|
+
INCLUDE = "include"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class CheckResult:
|
|
39
|
+
"""Outcome of one check inside a chain."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
passed: bool
|
|
43
|
+
reason: str = ""
|
|
44
|
+
control: Control = Control.REQUIRED
|
|
45
|
+
duration_ms: float = 0.0
|
|
46
|
+
|
|
47
|
+
def to_dict(self) -> dict:
|
|
48
|
+
return {
|
|
49
|
+
"name": self.name,
|
|
50
|
+
"passed": self.passed,
|
|
51
|
+
"reason": self.reason,
|
|
52
|
+
"control": self.control.value,
|
|
53
|
+
"duration_ms": round(self.duration_ms, 3),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class ChainDecision:
|
|
59
|
+
"""Aggregate outcome of evaluating one named chain."""
|
|
60
|
+
|
|
61
|
+
chain: str
|
|
62
|
+
passed: bool
|
|
63
|
+
results: tuple[CheckResult, ...] = field(default_factory=tuple)
|
|
64
|
+
reasons: tuple[str, ...] = field(default_factory=tuple)
|
|
65
|
+
abort_reason: str = ""
|
|
66
|
+
|
|
67
|
+
def to_dict(self) -> dict:
|
|
68
|
+
return {
|
|
69
|
+
"chain": self.chain,
|
|
70
|
+
"passed": self.passed,
|
|
71
|
+
"abort_reason": self.abort_reason,
|
|
72
|
+
"results": [r.to_dict() for r in self.results],
|
|
73
|
+
"reasons": list(self.reasons),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def fired_rules(self) -> list[str]:
|
|
77
|
+
"""Return the names of checks that failed (or the `sufficient` that short-circuited)."""
|
|
78
|
+
return [r.name for r in self.results if not r.passed or r.control == Control.SUFFICIENT]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# A Check is a callable that returns a CheckResult.
|
|
82
|
+
Check = Callable[[dict[str, Any], dict[str, Any]], CheckResult]
|
|
83
|
+
|
|
84
|
+
# A Chain is an ordered list of (control, check) pairs.
|
|
85
|
+
Chain = Sequence[tuple[Control, Check]]
|
|
86
|
+
|
|
87
|
+
# get_chain resolves an include reference to its chain definition.
|
|
88
|
+
ChainRegistry = Callable[[str], Chain | None]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def evaluate_chain(
|
|
92
|
+
chain_name: str,
|
|
93
|
+
checks: Chain,
|
|
94
|
+
tool_call: dict[str, Any],
|
|
95
|
+
context: dict[str, Any] | None = None,
|
|
96
|
+
*,
|
|
97
|
+
registry: ChainRegistry | None = None,
|
|
98
|
+
) -> ChainDecision:
|
|
99
|
+
"""Evaluate a chain of checks against a tool call.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
chain_name: Display name for this chain.
|
|
103
|
+
checks: Ordered (control, check) pairs.
|
|
104
|
+
tool_call: The tool invocation being gated. Shape:
|
|
105
|
+
{"tool": "algo_cli.shell.write", "args": {...}, "tier": "tier2", ...}
|
|
106
|
+
context: Session-level state (e.g. cwd, env, user).
|
|
107
|
+
registry: Optional callback that returns a named chain for INCLUDE.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
A ChainDecision with per-check results, the overall verdict, and
|
|
111
|
+
structured reasons suitable for the audit log.
|
|
112
|
+
"""
|
|
113
|
+
ctx = context or {}
|
|
114
|
+
results: list[CheckResult] = []
|
|
115
|
+
abort_reason = ""
|
|
116
|
+
|
|
117
|
+
for control, check in checks:
|
|
118
|
+
# INCLUDE — recursively evaluate a registered chain.
|
|
119
|
+
if control == Control.INCLUDE:
|
|
120
|
+
if registry is None:
|
|
121
|
+
results.append(CheckResult(
|
|
122
|
+
name=getattr(check, "__name__", "<include>"),
|
|
123
|
+
passed=False,
|
|
124
|
+
reason="include: no registry provided",
|
|
125
|
+
control=Control.INCLUDE,
|
|
126
|
+
))
|
|
127
|
+
continue
|
|
128
|
+
sub_chain = registry(getattr(check, "__name__", ""))
|
|
129
|
+
if sub_chain is None:
|
|
130
|
+
results.append(CheckResult(
|
|
131
|
+
name=getattr(check, "__name__", "<include>"),
|
|
132
|
+
passed=False,
|
|
133
|
+
reason="include: chain not registered",
|
|
134
|
+
control=Control.INCLUDE,
|
|
135
|
+
))
|
|
136
|
+
continue
|
|
137
|
+
sub = evaluate_chain(
|
|
138
|
+
getattr(check, "__name__", "include"),
|
|
139
|
+
sub_chain,
|
|
140
|
+
tool_call,
|
|
141
|
+
ctx,
|
|
142
|
+
registry=registry,
|
|
143
|
+
)
|
|
144
|
+
results.extend(sub.results)
|
|
145
|
+
# The include chain must itself pass; if it doesn't, propagate
|
|
146
|
+
# the failure but continue to the next check.
|
|
147
|
+
if not sub.passed:
|
|
148
|
+
results.append(CheckResult(
|
|
149
|
+
name=getattr(check, "__name__", "include"),
|
|
150
|
+
passed=False,
|
|
151
|
+
reason=f"included chain failed: {sub.abort_reason or 'see results'}",
|
|
152
|
+
control=Control.INCLUDE,
|
|
153
|
+
))
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
# Regular check.
|
|
157
|
+
try:
|
|
158
|
+
result = check(tool_call, ctx)
|
|
159
|
+
except Exception as exc: # noqa: BLE001
|
|
160
|
+
result = CheckResult(
|
|
161
|
+
name=getattr(check, "__name__", "<check>"),
|
|
162
|
+
passed=False,
|
|
163
|
+
reason=f"check raised: {type(exc).__name__}: {exc}",
|
|
164
|
+
control=control,
|
|
165
|
+
)
|
|
166
|
+
result.control = control
|
|
167
|
+
results.append(result)
|
|
168
|
+
|
|
169
|
+
# REQUISITE — abort on failure.
|
|
170
|
+
if not result.passed and control == Control.REQUISITE:
|
|
171
|
+
abort_reason = f"requisite failed: {result.name}: {result.reason}"
|
|
172
|
+
break
|
|
173
|
+
|
|
174
|
+
# SUFFICIENT — pass short-circuits the chain.
|
|
175
|
+
if result.passed and control == Control.SUFFICIENT:
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
passed = (not abort_reason) and all(r.passed for r in results)
|
|
179
|
+
reasons = tuple(r.reason for r in results if r.reason)
|
|
180
|
+
return ChainDecision(
|
|
181
|
+
chain=chain_name,
|
|
182
|
+
passed=passed,
|
|
183
|
+
results=tuple(results),
|
|
184
|
+
reasons=reasons,
|
|
185
|
+
abort_reason=abort_reason,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# --- Built-in checks -------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
def tier_check(min_tier: str) -> Check:
|
|
192
|
+
"""Check that the call's tier is at least `min_tier`."""
|
|
193
|
+
tier_rank = {"tier0": 0, "tier1": 1, "tier2": 2, "tier3": 3}
|
|
194
|
+
min_rank = tier_rank.get(min_tier, 0)
|
|
195
|
+
|
|
196
|
+
def _check(tool_call: dict[str, Any], _ctx: dict[str, Any]) -> CheckResult:
|
|
197
|
+
actual = tool_call.get("tier", "tier0")
|
|
198
|
+
actual_rank = tier_rank.get(actual, 0)
|
|
199
|
+
passed = actual_rank >= min_rank
|
|
200
|
+
return CheckResult(
|
|
201
|
+
name=f"tier(min={min_tier})",
|
|
202
|
+
passed=passed,
|
|
203
|
+
reason="" if passed else f"call tier {actual} < required {min_tier}",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
_check.__name__ = f"tier_{min_tier}"
|
|
207
|
+
return _check
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def path_allowlist_check(allow: list[str] | tuple[str, ...], block: list[str] | tuple[str, ...] = ()) -> Check:
|
|
211
|
+
"""Check that the call's path is in the allowlist and not in the blocklist."""
|
|
212
|
+
allow_t = tuple(allow)
|
|
213
|
+
block_t = tuple(block)
|
|
214
|
+
|
|
215
|
+
def _check(tool_call: dict[str, Any], _ctx: dict[str, Any]) -> CheckResult:
|
|
216
|
+
path = (tool_call.get("path") or tool_call.get("args", {}).get("path") or "").strip()
|
|
217
|
+
if not path:
|
|
218
|
+
return CheckResult(name="path_allowlist", passed=False, reason="no path in call")
|
|
219
|
+
if any(path.startswith(b) for b in block_t):
|
|
220
|
+
return CheckResult(name="path_allowlist", passed=False, reason=f"path {path!r} in blocklist")
|
|
221
|
+
if any(path.startswith(a) for a in allow_t):
|
|
222
|
+
return CheckResult(name="path_allowlist", passed=True)
|
|
223
|
+
return CheckResult(name="path_allowlist", passed=False, reason=f"path {path!r} not in allowlist")
|
|
224
|
+
|
|
225
|
+
_check.__name__ = "path_allowlist"
|
|
226
|
+
return _check
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def command_grep_check(deny: list[str] | tuple[str, ...]) -> Check:
|
|
230
|
+
"""Check that the call's command does not contain any denied substring."""
|
|
231
|
+
deny_t = tuple(deny)
|
|
232
|
+
|
|
233
|
+
def _check(tool_call: dict[str, Any], _ctx: dict[str, Any]) -> CheckResult:
|
|
234
|
+
cmd = (tool_call.get("command") or tool_call.get("args", {}).get("command") or "").lower()
|
|
235
|
+
for needle in deny_t:
|
|
236
|
+
if needle.lower() in cmd:
|
|
237
|
+
return CheckResult(
|
|
238
|
+
name="command_grep",
|
|
239
|
+
passed=False,
|
|
240
|
+
reason=f"command contains denied substring {needle!r}",
|
|
241
|
+
)
|
|
242
|
+
return CheckResult(name="command_grep", passed=True)
|
|
243
|
+
|
|
244
|
+
_check.__name__ = "command_grep"
|
|
245
|
+
return _check
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
__all__ = [
|
|
249
|
+
"Chain",
|
|
250
|
+
"ChainDecision",
|
|
251
|
+
"ChainRegistry",
|
|
252
|
+
"Check",
|
|
253
|
+
"CheckResult",
|
|
254
|
+
"Control",
|
|
255
|
+
"command_grep_check",
|
|
256
|
+
"evaluate_chain",
|
|
257
|
+
"path_allowlist_check",
|
|
258
|
+
"tier_check",
|
|
259
|
+
]
|