agent-devkit 0.1.0 → 0.1.6

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 (93) hide show
  1. package/README.md +42 -2
  2. package/package.json +1 -1
  3. package/runtime/README.md +119 -5
  4. package/runtime/agents/README.md +8 -0
  5. package/runtime/agents/execution-reviewer/AGENTS.md +8 -0
  6. package/runtime/agents/execution-reviewer/README.md +13 -0
  7. package/runtime/agents/execution-reviewer/agent.yaml +25 -0
  8. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/capability.yaml +15 -0
  9. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/decision-rules.md +6 -0
  10. package/runtime/agents/execution-reviewer/capabilities/review-agent-result/workflow.md +7 -0
  11. package/runtime/agents/execution-reviewer/capabilities/review-final-output/capability.yaml +15 -0
  12. package/runtime/agents/execution-reviewer/capabilities/review-final-output/decision-rules.md +6 -0
  13. package/runtime/agents/execution-reviewer/capabilities/review-final-output/workflow.md +7 -0
  14. package/runtime/agents/execution-reviewer/capabilities/review-plan/capability.yaml +15 -0
  15. package/runtime/agents/execution-reviewer/capabilities/review-plan/decision-rules.md +6 -0
  16. package/runtime/agents/execution-reviewer/capabilities/review-plan/workflow.md +7 -0
  17. package/runtime/agents/execution-reviewer/infra/README.md +4 -0
  18. package/runtime/agents/execution-reviewer/knowledge/context.md +6 -0
  19. package/runtime/agents/execution-reviewer/knowledge/system.md +18 -0
  20. package/runtime/agents/execution-reviewer/templates/README.md +4 -0
  21. package/runtime/agents/local-llm-operator/AGENTS.md +8 -0
  22. package/runtime/agents/local-llm-operator/README.md +12 -0
  23. package/runtime/agents/local-llm-operator/agent.yaml +26 -0
  24. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/capability.yaml +15 -0
  25. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/decision-rules.md +6 -0
  26. package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/workflow.md +7 -0
  27. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/capability.yaml +15 -0
  28. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/decision-rules.md +5 -0
  29. package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/workflow.md +6 -0
  30. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/capability.yaml +15 -0
  31. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/decision-rules.md +6 -0
  32. package/runtime/agents/local-llm-operator/capabilities/select-local-worker/workflow.md +7 -0
  33. package/runtime/agents/local-llm-operator/infra/README.md +4 -0
  34. package/runtime/agents/local-llm-operator/knowledge/context.md +6 -0
  35. package/runtime/agents/local-llm-operator/knowledge/system.md +18 -0
  36. package/runtime/agents/local-llm-operator/templates/README.md +4 -0
  37. package/runtime/agents/provider-configurator/AGENTS.md +8 -0
  38. package/runtime/agents/provider-configurator/README.md +5 -0
  39. package/runtime/agents/provider-configurator/agent.yaml +25 -0
  40. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/capability.yaml +15 -0
  41. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/decision-rules.md +6 -0
  42. package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/workflow.md +7 -0
  43. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/capability.yaml +15 -0
  44. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/decision-rules.md +6 -0
  45. package/runtime/agents/provider-configurator/capabilities/configure-provider-source/workflow.md +8 -0
  46. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/capability.yaml +15 -0
  47. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/decision-rules.md +6 -0
  48. package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/workflow.md +7 -0
  49. package/runtime/agents/provider-configurator/infra/README.md +5 -0
  50. package/runtime/agents/provider-configurator/knowledge/context.md +8 -0
  51. package/runtime/agents/provider-configurator/knowledge/system.md +19 -0
  52. package/runtime/agents/provider-configurator/templates/README.md +4 -0
  53. package/runtime/agents/task-orchestrator/AGENTS.md +8 -0
  54. package/runtime/agents/task-orchestrator/README.md +7 -0
  55. package/runtime/agents/task-orchestrator/agent.yaml +31 -0
  56. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/capability.yaml +15 -0
  57. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/decision-rules.md +6 -0
  58. package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/workflow.md +7 -0
  59. package/runtime/agents/task-orchestrator/capabilities/plan-task/capability.yaml +15 -0
  60. package/runtime/agents/task-orchestrator/capabilities/plan-task/decision-rules.md +6 -0
  61. package/runtime/agents/task-orchestrator/capabilities/plan-task/workflow.md +8 -0
  62. package/runtime/agents/task-orchestrator/capabilities/select-specialists/capability.yaml +15 -0
  63. package/runtime/agents/task-orchestrator/capabilities/select-specialists/decision-rules.md +6 -0
  64. package/runtime/agents/task-orchestrator/capabilities/select-specialists/workflow.md +7 -0
  65. package/runtime/agents/task-orchestrator/infra/README.md +5 -0
  66. package/runtime/agents/task-orchestrator/knowledge/context.md +9 -0
  67. package/runtime/agents/task-orchestrator/knowledge/system.md +20 -0
  68. package/runtime/agents/task-orchestrator/templates/README.md +4 -0
  69. package/runtime/cli/README.md +111 -10
  70. package/runtime/cli/aikit/__init__.py +1 -1
  71. package/runtime/cli/aikit/agent_executor.py +66 -0
  72. package/runtime/cli/aikit/agent_registry.py +80 -0
  73. package/runtime/cli/aikit/app_home.py +87 -2
  74. package/runtime/cli/aikit/audit.py +44 -0
  75. package/runtime/cli/aikit/configuration_orchestrator.py +291 -0
  76. package/runtime/cli/aikit/control_router.py +298 -0
  77. package/runtime/cli/aikit/decision_store.py +175 -0
  78. package/runtime/cli/aikit/diagnostics.py +2 -1
  79. package/runtime/cli/aikit/execution_reviewer.py +174 -0
  80. package/runtime/cli/aikit/fallback.py +48 -2
  81. package/runtime/cli/aikit/install.py +4 -3
  82. package/runtime/cli/aikit/llm.py +9 -0
  83. package/runtime/cli/aikit/local_llm_operator.py +112 -0
  84. package/runtime/cli/aikit/lock.py +4 -1
  85. package/runtime/cli/aikit/main.py +604 -21
  86. package/runtime/cli/aikit/model_router.py +79 -0
  87. package/runtime/cli/aikit/ollama.py +237 -0
  88. package/runtime/cli/aikit/orchestrator.py +535 -0
  89. package/runtime/cli/aikit/provider_wizard.py +19 -0
  90. package/runtime/cli/aikit/review_gate.py +40 -0
  91. package/runtime/cli/aikit/sessions.py +37 -0
  92. package/runtime/cli/aikit/sources.py +6 -2
  93. package/runtime/cli/aikit/wizard_state.py +503 -0
@@ -0,0 +1,79 @@
1
+ """Task-to-model routing decisions for Agent DevKit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any
7
+
8
+ from cli.aikit.llm import BACKENDS, doctor_backend, llm_preference, load_config
9
+ from cli.aikit.ollama import ollama_status
10
+
11
+
12
+ OPERATIONAL_PATTERN = re.compile(
13
+ r"(?i)\b(resum\w*|sumari\w*|classifi\w*|extra(?:i|ir|ia|cao|ção)\w*|normaliz\w*|compar\w*|logs?|rascunho|agrupe|agrupar)\b"
14
+ )
15
+ HIGH_LEVEL_PATTERN = re.compile(
16
+ r"(?i)\b(arquitet|decid|aprovar|reprovar|especifica|requisit|implemente|codigo|c[oó]digo|documento|automac|deploy|seguran)\b"
17
+ )
18
+
19
+
20
+ def build_model_plan(prompt: str, *, route: dict[str, Any] | None = None) -> dict[str, Any]:
21
+ config = load_config()
22
+ preference = llm_preference(config)
23
+ ollama = ollama_status()
24
+ ollama_backend = doctor_backend(BACKENDS["ollama"], config)
25
+ local_available = ollama.get("status") == "ok" or ollama_backend.get("status") == "ok"
26
+ operational = bool(OPERATIONAL_PATTERN.search(prompt))
27
+ high_level = bool(HIGH_LEVEL_PATTERN.search(prompt))
28
+ use_local = operational and local_available
29
+ return {
30
+ "kind": "model-plan",
31
+ "status": "planned",
32
+ "intent": route.get("intent") if route else "llm",
33
+ "primary_coordinators": coordinator_order(preference),
34
+ "local_llm_role": "operational-worker",
35
+ "local_llm_available": local_available,
36
+ "local_llm_provider": "ollama",
37
+ "local_llm_backend_configured": ollama_backend.get("status") == "ok",
38
+ "local_llm_runtime": {
39
+ "binary_status": ollama.get("status"),
40
+ "backend_status": ollama_backend.get("status"),
41
+ "model": ollama_backend.get("model"),
42
+ "base_url": ollama_backend.get("base_url"),
43
+ },
44
+ "local_llm_recommended": operational,
45
+ "local_llm_selected": use_local,
46
+ "delegation": {
47
+ "allowed": operational,
48
+ "selected": use_local,
49
+ "reason": local_reason(operational=operational, local_available=local_available, high_level=high_level),
50
+ "forbidden_actions": [
51
+ "final-review",
52
+ "external-write",
53
+ "permission-decision",
54
+ "architecture-decision",
55
+ "pr-approval",
56
+ ],
57
+ },
58
+ "fallback_order": preference.get("order") or [],
59
+ "route": route,
60
+ }
61
+
62
+
63
+ def coordinator_order(preference: dict[str, Any]) -> list[str]:
64
+ order = list(preference.get("order") or [])
65
+ preferred = [item for item in order if item in {"claude-code", "codex-cli"}]
66
+ for item in ("claude-code", "codex-cli"):
67
+ if item not in preferred:
68
+ preferred.append(item)
69
+ return preferred
70
+
71
+
72
+ def local_reason(*, operational: bool, local_available: bool, high_level: bool) -> str:
73
+ if operational and local_available and high_level:
74
+ return "Use Ollama for operational preprocessing; coordinator review remains mandatory."
75
+ if operational and local_available:
76
+ return "Task is operational and local Ollama is available."
77
+ if operational and not local_available:
78
+ return "Task is operational, but Ollama is not available; coordinator/API fallback should execute."
79
+ return "Task requires coordinator-level reasoning or review."
@@ -0,0 +1,237 @@
1
+ """Ollama local LLM discovery and model management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import platform
7
+ import shutil
8
+ import subprocess
9
+ import urllib.error
10
+ import urllib.request
11
+ from typing import Any
12
+
13
+
14
+ OLLAMA_TIMEOUT_SECONDS = 120
15
+ DEFAULT_BASE_URL = "http://localhost:11434"
16
+
17
+
18
+ def ollama_status(*, base_url: str = DEFAULT_BASE_URL) -> dict[str, Any]:
19
+ binary = shutil.which("ollama")
20
+ version = command_output(["ollama", "--version"]) if binary else None
21
+ daemon = daemon_status(base_url) if binary else {"status": "unknown", "message": "Ollama binary is not installed."}
22
+ models = list_local_models(binary_available=bool(binary))
23
+ status = "ok" if binary else "missing"
24
+ return {
25
+ "kind": "ollama-status",
26
+ "status": status,
27
+ "binary": binary,
28
+ "version": first_line(version),
29
+ "base_url": base_url,
30
+ "daemon": daemon,
31
+ "models": models,
32
+ "model_count": len(models),
33
+ "install_plan": install_plan() if not binary else None,
34
+ }
35
+
36
+
37
+ def ollama_models() -> dict[str, Any]:
38
+ binary = shutil.which("ollama")
39
+ items = list_local_models(binary_available=bool(binary))
40
+ return {
41
+ "kind": "ollama-models",
42
+ "status": "ok" if binary else "missing",
43
+ "binary": binary,
44
+ "items": items,
45
+ "recommended": recommended_models(installed={item["name"] for item in items}),
46
+ }
47
+
48
+
49
+ def ollama_pull(model: str | None, *, yes: bool = False, dry_run: bool = False) -> dict[str, Any]:
50
+ if not model:
51
+ raise ValueError("ollama pull requires a model name")
52
+ binary = shutil.which("ollama")
53
+ command = ["ollama", "pull", model]
54
+ if dry_run or not yes:
55
+ return {
56
+ "kind": "ollama-pull",
57
+ "status": "planned" if dry_run else "needs-confirmation",
58
+ "ok": bool(dry_run),
59
+ "model": model,
60
+ "binary": binary,
61
+ "command": command,
62
+ "dry_run": dry_run,
63
+ "yes": yes,
64
+ "message": "Use --yes to pull the model." if not dry_run and not yes else "Dry-run only.",
65
+ }
66
+ if not binary:
67
+ return {
68
+ "kind": "ollama-pull",
69
+ "status": "missing",
70
+ "ok": False,
71
+ "model": model,
72
+ "command": command,
73
+ "message": "Ollama binary not found in PATH.",
74
+ "install_plan": install_plan(),
75
+ "exit_code": 2,
76
+ }
77
+ process = run_command(command, timeout=OLLAMA_TIMEOUT_SECONDS * 5)
78
+ return {
79
+ "kind": "ollama-pull",
80
+ "status": "ok" if process.returncode == 0 else "failed",
81
+ "ok": process.returncode == 0,
82
+ "model": model,
83
+ "binary": binary,
84
+ "command": command,
85
+ "exit_code": process.returncode,
86
+ "stdout": safe_tail(process.stdout),
87
+ "stderr": safe_tail(process.stderr),
88
+ }
89
+
90
+
91
+ def ollama_update(*, yes: bool = False, dry_run: bool = False) -> dict[str, Any]:
92
+ command = update_command()
93
+ if dry_run or not yes:
94
+ return {
95
+ "kind": "ollama-update",
96
+ "status": "planned" if dry_run else "needs-confirmation",
97
+ "ok": bool(dry_run),
98
+ "command": command,
99
+ "dry_run": dry_run,
100
+ "yes": yes,
101
+ "message": "Use --yes to run the update command." if not dry_run and not yes else "Dry-run only.",
102
+ }
103
+ process = run_command(command, timeout=OLLAMA_TIMEOUT_SECONDS * 5, shell=True)
104
+ return {
105
+ "kind": "ollama-update",
106
+ "status": "ok" if process.returncode == 0 else "failed",
107
+ "ok": process.returncode == 0,
108
+ "command": command,
109
+ "exit_code": process.returncode,
110
+ "stdout": safe_tail(process.stdout),
111
+ "stderr": safe_tail(process.stderr),
112
+ }
113
+
114
+
115
+ def list_local_models(*, binary_available: bool) -> list[dict[str, Any]]:
116
+ if not binary_available:
117
+ return []
118
+ output = command_output(["ollama", "list"])
119
+ if not output:
120
+ return []
121
+ return parse_ollama_list(output)
122
+
123
+
124
+ def parse_ollama_list(output: str) -> list[dict[str, Any]]:
125
+ items: list[dict[str, Any]] = []
126
+ for line in output.splitlines():
127
+ stripped = line.strip()
128
+ if not stripped or stripped.lower().startswith("name "):
129
+ continue
130
+ parts = stripped.split()
131
+ if not parts:
132
+ continue
133
+ items.append(
134
+ {
135
+ "name": parts[0],
136
+ "id": parts[1] if len(parts) > 1 else None,
137
+ "size": parts[2] if len(parts) > 2 else None,
138
+ "modified": " ".join(parts[3:]) if len(parts) > 3 else None,
139
+ }
140
+ )
141
+ return items
142
+
143
+
144
+ def recommended_models(*, installed: set[str]) -> list[dict[str, Any]]:
145
+ catalog = [
146
+ ("qwen2.5-coder", "coding", "operational code reading and generation"),
147
+ ("deepseek-coder", "coding", "code analysis and mechanical refactors"),
148
+ ("deepseek-r1", "reasoning", "local reasoning drafts with mandatory review"),
149
+ ("llama3.2", "general", "general local summaries and classification"),
150
+ ("mistral", "general", "lightweight operational summaries"),
151
+ ("gemma", "classification", "small extraction and classification tasks"),
152
+ ]
153
+ return [
154
+ {
155
+ "name": name,
156
+ "family": family,
157
+ "recommended_for": recommended_for,
158
+ "installed": any(item == name or item.startswith(f"{name}:") for item in installed),
159
+ }
160
+ for name, family, recommended_for in catalog
161
+ ]
162
+
163
+
164
+ def daemon_status(base_url: str) -> dict[str, Any]:
165
+ url = base_url.rstrip("/") + "/api/tags"
166
+ try:
167
+ with urllib.request.urlopen(url, timeout=2) as response: # noqa: S310 - local configurable daemon URL.
168
+ raw = response.read().decode("utf-8", errors="replace")
169
+ except urllib.error.URLError as exc:
170
+ return {"status": "unavailable", "message": str(exc.reason)}
171
+ try:
172
+ payload = json.loads(raw)
173
+ except json.JSONDecodeError:
174
+ return {"status": "unknown", "message": "Ollama daemon returned non-JSON response."}
175
+ return {"status": "ok", "models": len(payload.get("models") or []) if isinstance(payload, dict) else None}
176
+
177
+
178
+ def install_plan() -> dict[str, Any]:
179
+ system = platform.system().lower()
180
+ if system == "darwin":
181
+ command = "brew install ollama"
182
+ elif system.startswith("win"):
183
+ command = "winget install Ollama.Ollama"
184
+ else:
185
+ command = "curl -fsSL https://ollama.com/install.sh | sh"
186
+ return {"platform": platform_key(), "command": command, "requires_opt_in": True}
187
+
188
+
189
+ def update_command() -> str:
190
+ system = platform.system().lower()
191
+ if system == "darwin":
192
+ return "brew upgrade ollama"
193
+ if system.startswith("win"):
194
+ return "winget upgrade Ollama.Ollama"
195
+ return "curl -fsSL https://ollama.com/install.sh | sh"
196
+
197
+
198
+ def platform_key() -> str:
199
+ system = platform.system().lower()
200
+ if system == "darwin":
201
+ return "darwin"
202
+ if system.startswith("win"):
203
+ return "windows"
204
+ return "linux"
205
+
206
+
207
+ def command_output(command: list[str]) -> str | None:
208
+ try:
209
+ process = run_command(command, timeout=10)
210
+ except OSError:
211
+ return None
212
+ if process.returncode != 0:
213
+ return None
214
+ return process.stdout.strip() or process.stderr.strip()
215
+
216
+
217
+ def run_command(command: list[str] | str, *, timeout: int, shell: bool = False) -> subprocess.CompletedProcess[str]:
218
+ return subprocess.run(
219
+ command,
220
+ shell=shell,
221
+ check=False,
222
+ text=True,
223
+ stdout=subprocess.PIPE,
224
+ stderr=subprocess.PIPE,
225
+ timeout=timeout,
226
+ )
227
+
228
+
229
+ def safe_tail(value: str | None, *, limit: int = 4000) -> str:
230
+ text = value or ""
231
+ return text[-limit:]
232
+
233
+
234
+ def first_line(value: str | None) -> str | None:
235
+ if not value:
236
+ return None
237
+ return value.splitlines()[0]