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,550 @@
1
+ """Built-in Agent Block pipeline definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from .config import CONFIG_DIR, _atomic_write_text
10
+ from .tool_policy import TOOL_GROUP_MAP, expand_tool_groups
11
+
12
+ try:
13
+ import tomllib
14
+ except ImportError: # Python 3.10: fall back to the tomli backport.
15
+ try:
16
+ import tomli as tomllib # type: ignore[no-redef]
17
+ except ImportError: # pragma: no cover - tomli is a declared 3.10 dependency.
18
+ tomllib = None # type: ignore[assignment]
19
+
20
+
21
+ NO_TOOLS: frozenset[str] = frozenset()
22
+ PLAN_TOOLS: frozenset[str] = expand_tool_groups(["read"])
23
+ READ_TOOLS: frozenset[str] = expand_tool_groups(["read", "web"])
24
+ IMPLEMENT_TOOLS: frozenset[str] = READ_TOOLS | expand_tool_groups(["write", "shell"])
25
+ REVIEW_TOOLS: frozenset[str] = READ_TOOLS | expand_tool_groups(["shell"])
26
+ DEFAULT_OUTPUT_LIMIT_CHARS = 3000
27
+ BLOCKS_CONFIG_PATH = CONFIG_DIR / "blocks.toml"
28
+ MAX_BLOCK_ITERATIONS = 24
29
+ REQUIRED_CHANGE_PROMPT = (
30
+ "File mutation policy (required-change block):\n"
31
+ "- You MUST use edit_file (find/replace) for surgical changes to existing files, and "
32
+ "write_file only for new files or full rewrites. Edit_file is faster and more "
33
+ "reliable than rewriting whole files.\n"
34
+ "- You MUST use write_file for any new file creation. Call write_file without "
35
+ "overwrite=True; that flag is only for overwriting existing files.\n"
36
+ "- When using edit_file, first call read_file to confirm the exact text, then include "
37
+ "2-5 lines of surrounding context in old_string so the match is unique. If the tool "
38
+ "reports an ambiguous match, tighten old_string with more context or pass "
39
+ "replace_all=True only when the rewrite is intentionally global.\n"
40
+ "- You MUST NOT use run_shell to create, edit, append to, or delete files. This includes "
41
+ "heredocs, output redirection (`> file`, `>> file`), `sed -i`, `awk -i`, `echo > file`, "
42
+ "`Set-Content`, `Add-Content`, `Out-File`, `New-Item -ItemType File`, `cp`/`mv` into the "
43
+ "workspace, and any other shell-based mutation. Shell-based file mutation will not be "
44
+ "counted as evidence of a change.\n"
45
+ "- Shell is permitted only for read-only verification (status, tests, lint, diff, grep, ls). "
46
+ "Direct Git or file mutations via shell require explicit approval and are not the preferred path.\n"
47
+ "- If edit_file and write_file are both unavailable or refuse, stop and report that the change cannot be made "
48
+ "within policy rather than routing edits through shell."
49
+ )
50
+
51
+
52
+ class BlocksConfigError(ValueError):
53
+ """Raised when a configured Agent Blocks pipeline cannot be used."""
54
+
55
+
56
+ @dataclass
57
+ class AgentBlock:
58
+ role: str
59
+ prompt: str
60
+ allowed_tools: frozenset[str] = NO_TOOLS
61
+ model: str | None = None
62
+ max_iterations: int = 8
63
+ requires_change: bool = False
64
+ status: str = "pending"
65
+ status_code: str = ""
66
+ status_reason: str = ""
67
+ output: str = ""
68
+ tool_calls: int = 0
69
+ duration_ms: float = 0.0
70
+ messages: list[dict] = field(default_factory=list)
71
+ context_output: str = ""
72
+ successful_writes: list[str] = field(default_factory=list)
73
+ git_evidence: str = ""
74
+ verification_warning: str = ""
75
+ failed_writes: list[str] = field(default_factory=list)
76
+ mutation_denied: bool = False
77
+ mutation_actions: list[str] = field(default_factory=list)
78
+ audit_evidence: str = ""
79
+
80
+
81
+ def _prompt(role: str, body: str) -> str:
82
+ tools = (
83
+ "You may use only the tools allowed for this block. "
84
+ "If no tools are available, reason from the supplied context only."
85
+ )
86
+ return (
87
+ f"You are the {role} block in a sequential terminal-agent pipeline.\n"
88
+ f"{tools}\n"
89
+ "Keep the output concise and directly useful to the next block.\n"
90
+ "When you are done, return Markdown starting with exactly:\n"
91
+ "## Block Output\n\n"
92
+ f"{body}"
93
+ )
94
+
95
+
96
+ def default_pipeline() -> list[AgentBlock]:
97
+ return [
98
+ AgentBlock(
99
+ role="plan",
100
+ allowed_tools=NO_TOOLS,
101
+ prompt=_prompt(
102
+ "plan",
103
+ "Create a short execution plan for the user's task. Identify assumptions, risks, and concrete next steps.",
104
+ ),
105
+ ),
106
+ AgentBlock(
107
+ role="research",
108
+ allowed_tools=READ_TOOLS,
109
+ prompt=_prompt(
110
+ "research",
111
+ "Gather only the information needed to execute the plan. Prefer local files and harness context before broad searches.",
112
+ ),
113
+ ),
114
+ AgentBlock(
115
+ role="implement",
116
+ allowed_tools=IMPLEMENT_TOOLS,
117
+ requires_change=True,
118
+ prompt=_prompt(
119
+ "implement",
120
+ "Perform the implementation or operational work. For any file creation, "
121
+ "use write_file. For surgical edits to an existing file, use edit_file "
122
+ "(find/replace) with 2-5 lines of surrounding context in old_string to "
123
+ "make the match unique. Do not use run_shell to write, edit, append to, or "
124
+ "delete files; shell is for verification only.",
125
+ ),
126
+ ),
127
+ AgentBlock(
128
+ role="review",
129
+ allowed_tools=REVIEW_TOOLS,
130
+ prompt=_prompt(
131
+ "review",
132
+ "Review the implementation for correctness, safety, regressions, and missing verification. "
133
+ "For large workspaces, sample representative high-risk files and directories first; "
134
+ "do not exhaustively enumerate the full tree unless the user explicitly requests it. "
135
+ "Once you have enough evidence for concrete findings, stop searching and write the block output. "
136
+ "Never claim an implementation occurred without successful write evidence or independently verified changed file contents.",
137
+ ),
138
+ ),
139
+ AgentBlock(
140
+ role="final",
141
+ allowed_tools=NO_TOOLS,
142
+ prompt=_prompt(
143
+ "final",
144
+ "Produce the final user-facing answer from the previous block outputs. Be concise and mention verification performed. "
145
+ "Do not state that files changed unless the supplied evidence confirms it.",
146
+ ),
147
+ ),
148
+ ]
149
+
150
+
151
+ def code_change_pipeline() -> list[AgentBlock]:
152
+ return [
153
+ AgentBlock(
154
+ role="plan",
155
+ allowed_tools=PLAN_TOOLS,
156
+ max_iterations=4,
157
+ prompt=_prompt(
158
+ "plan",
159
+ "Inspect the local project structure as needed, then create a short implementation plan "
160
+ "grounded in actual file paths. Identify likely risks and verification steps.",
161
+ ),
162
+ ),
163
+ AgentBlock(
164
+ role="implement",
165
+ allowed_tools=IMPLEMENT_TOOLS,
166
+ max_iterations=12,
167
+ requires_change=True,
168
+ prompt=_prompt(
169
+ "implement",
170
+ "Perform the requested code or configuration change. Inspect only what is needed to locate the edit, "
171
+ "then execute it with edit_file (preferred) or write_file. "
172
+ "For edit_file: call read_file first to confirm the exact text, then pass old_string with 2-5 lines "
173
+ "of surrounding context to make the match unique. If the match is ambiguous, tighten old_string or "
174
+ "pass replace_all=True only when the rewrite is intentionally global. "
175
+ "For write_file: use overwrite=True only when the file already exists and you intend to replace it. "
176
+ "Do not use run_shell to write, edit, append to, or delete files; shell-based file mutation does not count "
177
+ "as a verified change. After writing, re-read the changed section and run focused verification where practical.",
178
+ ),
179
+ ),
180
+ AgentBlock(
181
+ role="review",
182
+ allowed_tools=REVIEW_TOOLS,
183
+ prompt=_prompt(
184
+ "review",
185
+ "Review the change for correctness, safety, regressions, and missing verification. "
186
+ "For large workspaces, sample the relevant surface and prioritize concrete findings "
187
+ "over exhaustive directory enumeration. Stop searching once findings can be supported. "
188
+ "Never claim files changed unless Git evidence or successful write evidence is present and the changed content is verified.",
189
+ ),
190
+ ),
191
+ AgentBlock(
192
+ role="final",
193
+ allowed_tools=NO_TOOLS,
194
+ prompt=_prompt(
195
+ "final",
196
+ "Produce the final user-facing answer from the previous block outputs. Include changed files and verification performed. "
197
+ "Do not state that files changed unless implementation Git/write evidence and review confirmation support it.",
198
+ ),
199
+ ),
200
+ ]
201
+
202
+
203
+ def research_pipeline() -> list[AgentBlock]:
204
+ return [
205
+ AgentBlock(
206
+ role="plan",
207
+ allowed_tools=NO_TOOLS,
208
+ prompt=_prompt(
209
+ "plan",
210
+ "Create a short research plan. Identify the facts to verify and the most reliable sources to inspect.",
211
+ ),
212
+ ),
213
+ AgentBlock(
214
+ role="research",
215
+ allowed_tools=READ_TOOLS,
216
+ prompt=_prompt(
217
+ "research",
218
+ "Gather and compare the information needed to answer the task. Prefer primary sources and local evidence when available.",
219
+ ),
220
+ ),
221
+ AgentBlock(
222
+ role="final",
223
+ allowed_tools=NO_TOOLS,
224
+ prompt=_prompt(
225
+ "final",
226
+ "Produce a concise final answer from the research output. Call out uncertainty and source limits.",
227
+ ),
228
+ ),
229
+ ]
230
+
231
+
232
+ def review_pipeline() -> list[AgentBlock]:
233
+ return [
234
+ AgentBlock(
235
+ role="review",
236
+ allowed_tools=REVIEW_TOOLS,
237
+ prompt=_prompt(
238
+ "review",
239
+ "Review the supplied target for correctness, safety, regressions, and missing tests. "
240
+ "Lead with concrete findings. For large repositories or knowledge bases, sample "
241
+ "representative high-risk areas rather than exhaustively listing every file. "
242
+ "Stop inspecting once the principal findings are evidenced. "
243
+ "Do not claim changes were made unless verified in the target files.",
244
+ ),
245
+ ),
246
+ AgentBlock(
247
+ role="final",
248
+ allowed_tools=NO_TOOLS,
249
+ prompt=_prompt(
250
+ "final",
251
+ "Produce the final review summary from the reviewer output. Keep findings first and avoid unrelated commentary.",
252
+ ),
253
+ ),
254
+ ]
255
+
256
+
257
+ PIPELINE_FACTORIES = {
258
+ "default": default_pipeline,
259
+ "code-change": code_change_pipeline,
260
+ "research": research_pipeline,
261
+ "review": review_pipeline,
262
+ }
263
+
264
+ STARTER_CONFIG = """version = 1
265
+
266
+ [pipelines.default]
267
+ description = "Standard implementation workflow"
268
+
269
+ [[pipelines.default.blocks]]
270
+ role = "plan"
271
+ prompt = "Create a short execution plan for the user's task. Identify assumptions, risks, and concrete next steps."
272
+ tools = []
273
+ max_iterations = 8
274
+
275
+ [[pipelines.default.blocks]]
276
+ role = "research"
277
+ prompt = "Gather only the information needed to execute the plan. Prefer local files and harness context before broad searches."
278
+ tools = ["read", "web"]
279
+ max_iterations = 8
280
+
281
+ [[pipelines.default.blocks]]
282
+ role = "implement"
283
+ prompt = "Perform the implementation or operational work. For new files, use write_file. For surgical edits to existing files, use edit_file (find/replace) with 2-5 lines of surrounding context in old_string to make the match unique. Do not use run_shell to write, edit, append to, or delete files; shell is for verification only."
284
+ tools = ["read", "web", "write", "shell"]
285
+ max_iterations = 8
286
+ requires_change = true
287
+
288
+ [[pipelines.default.blocks]]
289
+ role = "review"
290
+ prompt = "Review the implementation for correctness, safety, regressions, and missing verification. For large workspaces, sample representative high-risk areas rather than exhaustively enumerating the full tree. Stop searching once findings can be supported. Never claim an implementation occurred without successful write evidence or independently verified changed file contents."
291
+ tools = ["read", "web", "shell"]
292
+ max_iterations = 8
293
+
294
+ [[pipelines.default.blocks]]
295
+ role = "final"
296
+ prompt = "Produce the final user-facing answer from previous block outputs. Mention verification performed. Do not state that files changed unless the supplied evidence confirms it."
297
+ tools = []
298
+ max_iterations = 8
299
+
300
+ [pipelines.code-change]
301
+ description = "Implementation workflow without a dedicated research pass"
302
+
303
+ [[pipelines.code-change.blocks]]
304
+ role = "plan"
305
+ prompt = "Inspect the local project structure as needed, then create a short implementation plan grounded in actual file paths. Identify likely risks and verification steps."
306
+ tools = ["read"]
307
+ max_iterations = 4
308
+
309
+ [[pipelines.code-change.blocks]]
310
+ role = "implement"
311
+ prompt = "Perform the requested code or configuration change. Inspect only what is needed to locate the edit, then execute it with edit_file (preferred) or write_file. For edit_file: call read_file first to confirm the exact text, then pass old_string with 2-5 lines of surrounding context to make the match unique. If the match is ambiguous, tighten old_string or pass replace_all=True only when the rewrite is intentionally global. For write_file: use overwrite=True only when the file already exists and you intend to replace it. Do not use run_shell to write, edit, append to, or delete files; shell-based file mutation does not count as a verified change. After writing, re-read the changed section and run focused verification where practical."
312
+ tools = ["read", "web", "write", "shell"]
313
+ max_iterations = 12
314
+ requires_change = true
315
+
316
+ [[pipelines.code-change.blocks]]
317
+ role = "review"
318
+ prompt = "Review the change for correctness, safety, regressions, and missing verification. For large workspaces, sample relevant high-risk areas rather than exhaustively enumerating the full tree. Stop searching once findings can be supported. Never claim files changed unless Git evidence or successful write evidence is present and the changed content is verified."
319
+ tools = ["read", "web", "shell"]
320
+ max_iterations = 8
321
+
322
+ [[pipelines.code-change.blocks]]
323
+ role = "final"
324
+ prompt = "Produce the final user-facing answer with changed files and verification performed. Do not state that files changed unless implementation Git/write evidence and review confirmation support it."
325
+ tools = []
326
+ max_iterations = 8
327
+
328
+ [pipelines.research]
329
+ description = "Focused research workflow"
330
+
331
+ [[pipelines.research.blocks]]
332
+ role = "plan"
333
+ prompt = "Create a short research plan and identify reliable sources to inspect."
334
+ tools = []
335
+ max_iterations = 8
336
+
337
+ [[pipelines.research.blocks]]
338
+ role = "research"
339
+ prompt = "Gather and compare information needed to answer the task."
340
+ tools = ["read", "web"]
341
+ max_iterations = 8
342
+
343
+ [[pipelines.research.blocks]]
344
+ role = "final"
345
+ prompt = "Produce a concise final answer and call out uncertainty or source limits."
346
+ tools = []
347
+ max_iterations = 8
348
+
349
+ [pipelines.review]
350
+ description = "Focused review workflow"
351
+
352
+ [[pipelines.review.blocks]]
353
+ role = "review"
354
+ prompt = "Review the target for correctness, safety, regressions, and missing tests. Lead with findings and sample representative high-risk areas for large workspaces. Stop inspecting once principal findings are evidenced. Do not claim changes were made unless verified in the target files."
355
+ tools = ["read", "web", "shell"]
356
+ max_iterations = 8
357
+
358
+ [[pipelines.review.blocks]]
359
+ role = "final"
360
+ prompt = "Produce the final review summary with findings first."
361
+ tools = []
362
+ max_iterations = 8
363
+ """
364
+
365
+
366
+ def pipeline_names() -> list[str]:
367
+ return sorted(PIPELINE_FACTORIES)
368
+
369
+
370
+ def builtin_pipeline_by_name(name: str) -> list[AgentBlock]:
371
+ key = (name or "default").strip().lower()
372
+ try:
373
+ pipeline = PIPELINE_FACTORIES[key]()
374
+ _validate_pipeline_contract(key, pipeline)
375
+ return pipeline
376
+ except KeyError as exc:
377
+ available = ", ".join(pipeline_names())
378
+ raise ValueError(f"Unknown pipeline '{name}'. Available: {available}") from exc
379
+
380
+
381
+ def _validate_pipeline_contract(pipeline_name: str, blocks: list[AgentBlock], *, start_index: int = 0) -> None:
382
+ for index, block in enumerate(blocks, start=start_index):
383
+ if block.requires_change and "write_file" not in block.allowed_tools:
384
+ raise BlocksConfigError(
385
+ f"Error in blocks.toml: pipeline '{pipeline_name}', block {index + 1} has "
386
+ "requires_change=true but does not include the 'write' tool group. "
387
+ "requires_change blocks must be able to use write_file."
388
+ )
389
+
390
+
391
+ def _validate_block(pipeline_name: str, index: int, data: Any) -> AgentBlock:
392
+ label = f"pipeline '{pipeline_name}', block {index + 1}"
393
+ if not isinstance(data, dict):
394
+ raise BlocksConfigError(f"Error in blocks.toml: {label} must be a table.")
395
+ role = data.get("role")
396
+ prompt = data.get("prompt")
397
+ groups = data.get("tools", [])
398
+ max_iterations = data.get("max_iterations", 8)
399
+ model = data.get("model")
400
+ requires_change = data.get("requires_change", False)
401
+ if not isinstance(role, str) or not role.strip():
402
+ raise BlocksConfigError(f"Error in blocks.toml: {label} requires a non-empty role.")
403
+ if not isinstance(prompt, str) or not prompt.strip():
404
+ raise BlocksConfigError(f"Error in blocks.toml: {label} requires a non-empty prompt.")
405
+ if not isinstance(groups, list) or not all(isinstance(group, str) for group in groups):
406
+ raise BlocksConfigError(f"Error in blocks.toml: {label} tools must be a list of tool-group names.")
407
+ unknown = sorted(set(groups) - set(TOOL_GROUP_MAP))
408
+ if unknown:
409
+ available = ", ".join(sorted(TOOL_GROUP_MAP))
410
+ raise BlocksConfigError(
411
+ f"Error in blocks.toml: {label} has unknown tool group '{unknown[0]}'. Available: {available}."
412
+ )
413
+ if isinstance(max_iterations, bool) or not isinstance(max_iterations, int) or not 1 <= max_iterations <= MAX_BLOCK_ITERATIONS:
414
+ raise BlocksConfigError(
415
+ f"Error in blocks.toml: {label} max_iterations must be an integer from 1 to {MAX_BLOCK_ITERATIONS}."
416
+ )
417
+ if model is not None and (not isinstance(model, str) or not model.strip()):
418
+ raise BlocksConfigError(f"Error in blocks.toml: {label} model must be a non-empty string when set.")
419
+ if not isinstance(requires_change, bool):
420
+ raise BlocksConfigError(f"Error in blocks.toml: {label} requires_change must be true or false.")
421
+ block = AgentBlock(
422
+ role=role.strip(),
423
+ prompt=_prompt(role.strip(), prompt.strip()),
424
+ allowed_tools=expand_tool_groups(groups),
425
+ model=model.strip() if isinstance(model, str) else None,
426
+ max_iterations=max_iterations,
427
+ requires_change=requires_change,
428
+ )
429
+ _validate_pipeline_contract(pipeline_name, [block], start_index=index)
430
+ return block
431
+
432
+
433
+ def load_configured_pipelines(path: Path | None = None) -> dict[str, list[AgentBlock]]:
434
+ config_path = path or BLOCKS_CONFIG_PATH
435
+ if not config_path.exists():
436
+ return {}
437
+ if tomllib is None:
438
+ raise BlocksConfigError("blocks.toml requires Python 3.11+ tomllib; using built-in pipelines.")
439
+ try:
440
+ with config_path.open("rb") as handle:
441
+ data = tomllib.load(handle)
442
+ except tomllib.TOMLDecodeError as exc:
443
+ raise BlocksConfigError(f"Error in blocks.toml: {exc}") from exc
444
+ if data.get("version") != 1:
445
+ raise BlocksConfigError("Error in blocks.toml: version must be 1.")
446
+ pipelines = data.get("pipelines")
447
+ if not isinstance(pipelines, dict) or not pipelines:
448
+ raise BlocksConfigError("Error in blocks.toml: at least one [pipelines.NAME] table is required.")
449
+ unknown_names = sorted(set(pipelines) - set(PIPELINE_FACTORIES))
450
+ if unknown_names:
451
+ available = ", ".join(pipeline_names())
452
+ raise BlocksConfigError(
453
+ f"Error in blocks.toml: unsupported pipeline '{unknown_names[0]}'. Configurable names: {available}."
454
+ )
455
+ configured: dict[str, list[AgentBlock]] = {}
456
+ for name, pipeline_data in pipelines.items():
457
+ if not isinstance(pipeline_data, dict):
458
+ raise BlocksConfigError(f"Error in blocks.toml: pipeline '{name}' must be a table.")
459
+ blocks = pipeline_data.get("blocks")
460
+ if not isinstance(blocks, list) or not blocks:
461
+ raise BlocksConfigError(f"Error in blocks.toml: pipeline '{name}' requires at least one block.")
462
+ configured[name] = [_validate_block(name, index, block) for index, block in enumerate(blocks)]
463
+ return configured
464
+
465
+
466
+ def resolve_pipeline(name: str, path: Path | None = None) -> tuple[list[AgentBlock], str]:
467
+ key = (name or "default").strip().lower()
468
+ configured = load_configured_pipelines(path)
469
+ if key in configured:
470
+ return configured[key], "configured"
471
+ return builtin_pipeline_by_name(key), "built-in"
472
+
473
+
474
+ def pipeline_by_name(name: str, path: Path | None = None) -> list[AgentBlock]:
475
+ return resolve_pipeline(name, path)[0]
476
+
477
+
478
+ def write_starter_config(path: Path | None = None) -> Path:
479
+ config_path = path or BLOCKS_CONFIG_PATH
480
+ if config_path.exists():
481
+ raise FileExistsError(f"{config_path} already exists. Edit it directly or remove it before running /agent init.")
482
+ config_path.parent.mkdir(parents=True, exist_ok=True)
483
+ _atomic_write_text(config_path, STARTER_CONFIG)
484
+ return config_path
485
+
486
+
487
+ def allowed_tool_functions(block: AgentBlock, tool_map: dict[str, object]) -> list[object]:
488
+ return [tool_map[name] for name in sorted(block.allowed_tools) if name in tool_map]
489
+
490
+
491
+ def pipeline_context(task: str, completed: list[AgentBlock]) -> str:
492
+ parts = [f"## Original Task\n{task.strip()}"]
493
+ for block in completed:
494
+ output = (block.context_output or block.output).strip() or "(no output produced)"
495
+ parts.append(f"## Output from {block.role}\n{output}")
496
+ if block.status_reason:
497
+ parts.append(
498
+ "## Block Status\n"
499
+ f"Status: {block.status.upper()}\n"
500
+ f"Code: {block.status_code or '-'}\n"
501
+ f"Reason: {block.status_reason}"
502
+ )
503
+ if block.verification_warning:
504
+ parts.append(
505
+ "## Verification\n"
506
+ f"{block.verification_warning}"
507
+ )
508
+ if block.requires_change:
509
+ writes = ", ".join(block.successful_writes) if block.successful_writes else "(none recorded)"
510
+ parts.append(
511
+ "## Implementation Write Evidence\n"
512
+ f"Successful write_file targets: {writes}\n"
513
+ "Do not claim requested code changes were completed without confirming this evidence and the resulting file contents."
514
+ )
515
+ parts.append(
516
+ "## Implementation Git Evidence\n"
517
+ f"{block.git_evidence or 'No Git evidence snapshot was captured.'}\n"
518
+ "Treat only changes introduced during the implement block as verified implementation evidence."
519
+ )
520
+ elif block.audit_evidence:
521
+ parts.append(
522
+ "## Mutation Audit Evidence\n"
523
+ f"{block.audit_evidence}\n"
524
+ "This is audit-only evidence; it does not change the block status or prove requested implementation."
525
+ )
526
+ return "\n\n".join(parts)
527
+
528
+
529
+ def compact_block_output(output: str, limit_chars: int = DEFAULT_OUTPUT_LIMIT_CHARS) -> str:
530
+ """Compact block output for downstream context while preserving paragraphs."""
531
+ text = (output or "").strip()
532
+ if len(text) <= limit_chars:
533
+ return text
534
+
535
+ kept: list[str] = []
536
+ total = 0
537
+ for paragraph in text.split("\n\n"):
538
+ paragraph = paragraph.strip()
539
+ if not paragraph:
540
+ continue
541
+ next_total = total + len(paragraph) + (2 if kept else 0)
542
+ if next_total > limit_chars:
543
+ break
544
+ kept.append(paragraph)
545
+ total = next_total
546
+
547
+ compacted = "\n\n".join(kept).strip()
548
+ if not compacted:
549
+ compacted = text[:limit_chars].rstrip()
550
+ return f"{compacted}\n\n... (compacted from {len(text)} chars, {len(text.split())} words)"