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.
- package/README.md +42 -2
- package/package.json +1 -1
- package/runtime/README.md +119 -5
- package/runtime/agents/README.md +8 -0
- package/runtime/agents/execution-reviewer/AGENTS.md +8 -0
- package/runtime/agents/execution-reviewer/README.md +13 -0
- package/runtime/agents/execution-reviewer/agent.yaml +25 -0
- package/runtime/agents/execution-reviewer/capabilities/review-agent-result/capability.yaml +15 -0
- package/runtime/agents/execution-reviewer/capabilities/review-agent-result/decision-rules.md +6 -0
- package/runtime/agents/execution-reviewer/capabilities/review-agent-result/workflow.md +7 -0
- package/runtime/agents/execution-reviewer/capabilities/review-final-output/capability.yaml +15 -0
- package/runtime/agents/execution-reviewer/capabilities/review-final-output/decision-rules.md +6 -0
- package/runtime/agents/execution-reviewer/capabilities/review-final-output/workflow.md +7 -0
- package/runtime/agents/execution-reviewer/capabilities/review-plan/capability.yaml +15 -0
- package/runtime/agents/execution-reviewer/capabilities/review-plan/decision-rules.md +6 -0
- package/runtime/agents/execution-reviewer/capabilities/review-plan/workflow.md +7 -0
- package/runtime/agents/execution-reviewer/infra/README.md +4 -0
- package/runtime/agents/execution-reviewer/knowledge/context.md +6 -0
- package/runtime/agents/execution-reviewer/knowledge/system.md +18 -0
- package/runtime/agents/execution-reviewer/templates/README.md +4 -0
- package/runtime/agents/local-llm-operator/AGENTS.md +8 -0
- package/runtime/agents/local-llm-operator/README.md +12 -0
- package/runtime/agents/local-llm-operator/agent.yaml +26 -0
- package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/capability.yaml +15 -0
- package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/decision-rules.md +6 -0
- package/runtime/agents/local-llm-operator/capabilities/delegate-operational-task/workflow.md +7 -0
- package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/capability.yaml +15 -0
- package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/decision-rules.md +5 -0
- package/runtime/agents/local-llm-operator/capabilities/inspect-local-models/workflow.md +6 -0
- package/runtime/agents/local-llm-operator/capabilities/select-local-worker/capability.yaml +15 -0
- package/runtime/agents/local-llm-operator/capabilities/select-local-worker/decision-rules.md +6 -0
- package/runtime/agents/local-llm-operator/capabilities/select-local-worker/workflow.md +7 -0
- package/runtime/agents/local-llm-operator/infra/README.md +4 -0
- package/runtime/agents/local-llm-operator/knowledge/context.md +6 -0
- package/runtime/agents/local-llm-operator/knowledge/system.md +18 -0
- package/runtime/agents/local-llm-operator/templates/README.md +4 -0
- package/runtime/agents/provider-configurator/AGENTS.md +8 -0
- package/runtime/agents/provider-configurator/README.md +5 -0
- package/runtime/agents/provider-configurator/agent.yaml +25 -0
- package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/capability.yaml +15 -0
- package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/decision-rules.md +6 -0
- package/runtime/agents/provider-configurator/capabilities/collect-provider-credentials/workflow.md +7 -0
- package/runtime/agents/provider-configurator/capabilities/configure-provider-source/capability.yaml +15 -0
- package/runtime/agents/provider-configurator/capabilities/configure-provider-source/decision-rules.md +6 -0
- package/runtime/agents/provider-configurator/capabilities/configure-provider-source/workflow.md +8 -0
- package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/capability.yaml +15 -0
- package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/decision-rules.md +6 -0
- package/runtime/agents/provider-configurator/capabilities/validate-provider-readiness/workflow.md +7 -0
- package/runtime/agents/provider-configurator/infra/README.md +5 -0
- package/runtime/agents/provider-configurator/knowledge/context.md +8 -0
- package/runtime/agents/provider-configurator/knowledge/system.md +19 -0
- package/runtime/agents/provider-configurator/templates/README.md +4 -0
- package/runtime/agents/task-orchestrator/AGENTS.md +8 -0
- package/runtime/agents/task-orchestrator/README.md +7 -0
- package/runtime/agents/task-orchestrator/agent.yaml +31 -0
- package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/capability.yaml +15 -0
- package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/decision-rules.md +6 -0
- package/runtime/agents/task-orchestrator/capabilities/coordinate-execution/workflow.md +7 -0
- package/runtime/agents/task-orchestrator/capabilities/plan-task/capability.yaml +15 -0
- package/runtime/agents/task-orchestrator/capabilities/plan-task/decision-rules.md +6 -0
- package/runtime/agents/task-orchestrator/capabilities/plan-task/workflow.md +8 -0
- package/runtime/agents/task-orchestrator/capabilities/select-specialists/capability.yaml +15 -0
- package/runtime/agents/task-orchestrator/capabilities/select-specialists/decision-rules.md +6 -0
- package/runtime/agents/task-orchestrator/capabilities/select-specialists/workflow.md +7 -0
- package/runtime/agents/task-orchestrator/infra/README.md +5 -0
- package/runtime/agents/task-orchestrator/knowledge/context.md +9 -0
- package/runtime/agents/task-orchestrator/knowledge/system.md +20 -0
- package/runtime/agents/task-orchestrator/templates/README.md +4 -0
- package/runtime/cli/README.md +111 -10
- package/runtime/cli/aikit/__init__.py +1 -1
- package/runtime/cli/aikit/agent_executor.py +66 -0
- package/runtime/cli/aikit/agent_registry.py +80 -0
- package/runtime/cli/aikit/app_home.py +87 -2
- package/runtime/cli/aikit/audit.py +44 -0
- package/runtime/cli/aikit/configuration_orchestrator.py +291 -0
- package/runtime/cli/aikit/control_router.py +298 -0
- package/runtime/cli/aikit/decision_store.py +175 -0
- package/runtime/cli/aikit/diagnostics.py +2 -1
- package/runtime/cli/aikit/execution_reviewer.py +174 -0
- package/runtime/cli/aikit/fallback.py +48 -2
- package/runtime/cli/aikit/install.py +4 -3
- package/runtime/cli/aikit/llm.py +9 -0
- package/runtime/cli/aikit/local_llm_operator.py +112 -0
- package/runtime/cli/aikit/lock.py +4 -1
- package/runtime/cli/aikit/main.py +604 -21
- package/runtime/cli/aikit/model_router.py +79 -0
- package/runtime/cli/aikit/ollama.py +237 -0
- package/runtime/cli/aikit/orchestrator.py +535 -0
- package/runtime/cli/aikit/provider_wizard.py +19 -0
- package/runtime/cli/aikit/review_gate.py +40 -0
- package/runtime/cli/aikit/sessions.py +37 -0
- package/runtime/cli/aikit/sources.py +6 -2
- package/runtime/cli/aikit/wizard_state.py +503 -0
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
from cli.aikit.credentials import CredentialResolverError
|
|
9
|
-
from cli.aikit.providers import ProviderRegistryError, provider_status_with_credentials
|
|
9
|
+
from cli.aikit.providers import ProviderRegistryError, load_providers, provider_status_with_credentials
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
READY_PROVIDER_STATUSES = {"ok", "unknown"}
|
|
@@ -20,9 +20,11 @@ FALLBACKS = {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def evaluate_provider_requirements(root: Path, capability: dict[str, Any]) -> dict[str, Any]:
|
|
23
|
+
def evaluate_provider_requirements(root: Path, capability: dict[str, Any], args: list[str] | None = None) -> dict[str, Any]:
|
|
24
24
|
"""Evaluate `requires.providers` without returning credential values."""
|
|
25
25
|
requirements = provider_requirements(capability)
|
|
26
|
+
if not requirements and not has_fixture_arg(args or []):
|
|
27
|
+
requirements = infer_provider_requirements(root, capability)
|
|
26
28
|
providers = {
|
|
27
29
|
"used": [],
|
|
28
30
|
"missing": [],
|
|
@@ -75,6 +77,50 @@ def provider_requirements(capability: dict[str, Any]) -> list[dict[str, Any]]:
|
|
|
75
77
|
return [item for item in providers if isinstance(item, dict)]
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
def infer_provider_requirements(root: Path, capability: dict[str, Any]) -> list[dict[str, Any]]:
|
|
81
|
+
capability_id = str(capability.get("id") or "")
|
|
82
|
+
if "." not in capability_id:
|
|
83
|
+
return []
|
|
84
|
+
agent_id, short_capability = capability_id.rsplit(".", 1)
|
|
85
|
+
candidates = {f"{agent_id}/{short_capability}", capability_id}
|
|
86
|
+
requirements: list[dict[str, Any]] = []
|
|
87
|
+
try:
|
|
88
|
+
providers = load_providers(root)
|
|
89
|
+
except ProviderRegistryError:
|
|
90
|
+
return []
|
|
91
|
+
for provider in providers:
|
|
92
|
+
capabilities = provider.get("capabilities") if isinstance(provider.get("capabilities"), dict) else {}
|
|
93
|
+
matched_mode = None
|
|
94
|
+
for mode in ("read", "write"):
|
|
95
|
+
declared = capabilities.get(mode) if isinstance(capabilities, dict) else []
|
|
96
|
+
if any(str(item) in candidates for item in (declared or [])):
|
|
97
|
+
matched_mode = mode
|
|
98
|
+
break
|
|
99
|
+
if not matched_mode:
|
|
100
|
+
continue
|
|
101
|
+
fallbacks = list(provider.get("fallbacks", []) or [])
|
|
102
|
+
requirements.append(
|
|
103
|
+
{
|
|
104
|
+
"id": provider.get("id"),
|
|
105
|
+
"mode": "required",
|
|
106
|
+
"fallback": fallbacks[0] if fallbacks else "blocked",
|
|
107
|
+
"purpose": f"inferred from provider registry for {agent_id}/{short_capability}",
|
|
108
|
+
"access": matched_mode,
|
|
109
|
+
"inferred": True,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
return requirements
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def has_fixture_arg(args: list[str]) -> bool:
|
|
116
|
+
for index, item in enumerate(args):
|
|
117
|
+
if item == "--fixture" and index + 1 < len(args):
|
|
118
|
+
return True
|
|
119
|
+
if item.startswith("--fixture="):
|
|
120
|
+
return True
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
|
|
78
124
|
def provider_detail(requirement: dict[str, Any], status_item: dict[str, Any]) -> dict[str, Any]:
|
|
79
125
|
provider_id = str(requirement.get("id") or "")
|
|
80
126
|
return {
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from cli.aikit import __version__
|
|
12
|
-
from cli.aikit.lock import lock_path, write_lock
|
|
12
|
+
from cli.aikit.lock import GLOBAL_RUNTIME_DIR, PROJECT_RUNTIME_DIR, lock_path, write_lock
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
HOST_ALIASES = {
|
|
@@ -46,7 +46,7 @@ def install_runtime(
|
|
|
46
46
|
hosts = resolve_hosts(host)
|
|
47
47
|
root = root.resolve()
|
|
48
48
|
base = resolve_base(scope, target=target, home=home)
|
|
49
|
-
runtime_dir = base / "
|
|
49
|
+
runtime_dir = base / (GLOBAL_RUNTIME_DIR if scope == "global" else PROJECT_RUNTIME_DIR)
|
|
50
50
|
bin_dir = runtime_dir / "bin"
|
|
51
51
|
config_path = runtime_dir / "config.yaml"
|
|
52
52
|
runtime_lock_path = lock_path(base, scope)
|
|
@@ -256,7 +256,8 @@ def write_runtime_config(path: Path, root: Path, scope: str, hosts: tuple[str, .
|
|
|
256
256
|
|
|
257
257
|
|
|
258
258
|
def next_steps(scope: str, hosts: tuple[str, ...]) -> list[str]:
|
|
259
|
-
|
|
259
|
+
runtime_dir_name = GLOBAL_RUNTIME_DIR if scope == "global" else PROJECT_RUNTIME_DIR
|
|
260
|
+
steps = [f"Add `{runtime_dir_name}/bin` to PATH when you want to call the installed `agent` directly."]
|
|
260
261
|
steps.append("Run `agent doctor --json` to validate the local runtime.")
|
|
261
262
|
if "codex" in hosts:
|
|
262
263
|
steps.append("Restart or reload Codex so it can discover the AI DevKit plugin/skill.")
|
package/runtime/cli/aikit/llm.py
CHANGED
|
@@ -159,6 +159,8 @@ def list_backends() -> dict[str, Any]:
|
|
|
159
159
|
default_backend = config.get("llm", {}).get("default")
|
|
160
160
|
configured = set(config.get("llm", {}).get("backends", {}))
|
|
161
161
|
preference = llm_preference(config)
|
|
162
|
+
from cli.aikit.decision_store import get_decision
|
|
163
|
+
|
|
162
164
|
return {
|
|
163
165
|
"kind": "llm-backends",
|
|
164
166
|
"config_path": str(config_path()),
|
|
@@ -179,6 +181,8 @@ def list_backends() -> dict[str, Any]:
|
|
|
179
181
|
"default_model": backend.default_model,
|
|
180
182
|
"command": backend.command,
|
|
181
183
|
"notes": backend.notes,
|
|
184
|
+
"decision": get_decision("llms", backend.id),
|
|
185
|
+
"enabled": not bool((get_decision("llms", backend.id) or {}).get("state") in {"disabled_by_user", "denied_by_user"}),
|
|
182
186
|
}
|
|
183
187
|
for backend in BACKENDS.values()
|
|
184
188
|
],
|
|
@@ -445,6 +449,11 @@ def candidate_backend_ids(
|
|
|
445
449
|
llm = config.get("llm") if isinstance(config.get("llm"), dict) else {}
|
|
446
450
|
configured = llm.get("backends") if isinstance(llm.get("backends"), dict) else {}
|
|
447
451
|
configured_ids = [backend_id for backend_id in configured if backend_id in BACKENDS]
|
|
452
|
+
if not configured_ids:
|
|
453
|
+
return []
|
|
454
|
+
from cli.aikit.decision_store import is_disabled
|
|
455
|
+
|
|
456
|
+
configured_ids = [backend_id for backend_id in configured_ids if not is_disabled("llms", backend_id)]
|
|
448
457
|
if not configured_ids:
|
|
449
458
|
return []
|
|
450
459
|
preference = llm_preference(config)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Runtime execution for the local-llm-operator agent."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from cli.aikit.llm import invoke_agent_prompt
|
|
8
|
+
from cli.aikit.memory import redact_secrets
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
FORBIDDEN_DELEGATION_MARKERS = (
|
|
12
|
+
"aprove",
|
|
13
|
+
"aprovar",
|
|
14
|
+
"reprove",
|
|
15
|
+
"reprovar",
|
|
16
|
+
"deploy",
|
|
17
|
+
"delete",
|
|
18
|
+
"excluir",
|
|
19
|
+
"execute escrita",
|
|
20
|
+
"permissao",
|
|
21
|
+
"permissão",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[str, Any]:
|
|
26
|
+
"""Execute a bounded operational task with Ollama when the model plan selected it."""
|
|
27
|
+
delegation = model_plan.get("delegation") if isinstance(model_plan.get("delegation"), dict) else {}
|
|
28
|
+
if not model_plan.get("local_llm_selected") or not delegation.get("selected"):
|
|
29
|
+
return skipped("not-selected", "Local LLM delegation was not selected for this prompt.", model_plan=model_plan)
|
|
30
|
+
lowered = prompt.lower()
|
|
31
|
+
if any(marker in lowered for marker in FORBIDDEN_DELEGATION_MARKERS):
|
|
32
|
+
return skipped("forbidden", "Prompt contains an action that local LLM workers cannot execute.", model_plan=model_plan)
|
|
33
|
+
delegated_prompt = build_delegated_prompt(prompt, model_plan)
|
|
34
|
+
result = invoke_agent_prompt(
|
|
35
|
+
delegated_prompt,
|
|
36
|
+
"ollama",
|
|
37
|
+
public_name="Local LLM Operator",
|
|
38
|
+
allow_fallback=False,
|
|
39
|
+
)
|
|
40
|
+
payload = {
|
|
41
|
+
"kind": "local-llm-execution",
|
|
42
|
+
"agent_id": "local-llm-operator",
|
|
43
|
+
"capability_id": "delegate-operational-task",
|
|
44
|
+
"capability": "local-llm-operator.delegate-operational-task",
|
|
45
|
+
"status": "ok" if result.get("ok") else result.get("status", "failed"),
|
|
46
|
+
"ok": bool(result.get("ok")),
|
|
47
|
+
"llm_backend": result.get("llm_backend"),
|
|
48
|
+
"model_provider": "ollama",
|
|
49
|
+
"delegated_prompt": redact_secrets(delegated_prompt),
|
|
50
|
+
"response": result.get("response"),
|
|
51
|
+
"result": sanitize_llm_result(result),
|
|
52
|
+
"requires_review": True,
|
|
53
|
+
}
|
|
54
|
+
if not result.get("ok"):
|
|
55
|
+
payload["message"] = result.get("message") or "Local LLM delegation failed."
|
|
56
|
+
return payload
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def build_delegated_prompt(prompt: str, model_plan: dict[str, Any]) -> str:
|
|
60
|
+
forbidden = ", ".join((model_plan.get("delegation") or {}).get("forbidden_actions") or [])
|
|
61
|
+
return "\n".join(
|
|
62
|
+
[
|
|
63
|
+
"Execute apenas a parte operacional da tarefa abaixo.",
|
|
64
|
+
"Nao tome decisao final, nao aprove, nao escreva externamente e nao revise a entrega final.",
|
|
65
|
+
f"Acoes proibidas: {forbidden}.",
|
|
66
|
+
"Retorne um resumo estruturado, evidencias extraidas, lacunas e confianca.",
|
|
67
|
+
"",
|
|
68
|
+
"Tarefa original:",
|
|
69
|
+
redact_secrets(prompt),
|
|
70
|
+
]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def enrich_prompt_with_local_result(prompt: str, local_execution: dict[str, Any]) -> str:
|
|
75
|
+
if not local_execution.get("ok") or not local_execution.get("response"):
|
|
76
|
+
return prompt
|
|
77
|
+
return "\n".join(
|
|
78
|
+
[
|
|
79
|
+
prompt,
|
|
80
|
+
"",
|
|
81
|
+
"Contexto operacional produzido pelo local-llm-operator/Ollama:",
|
|
82
|
+
str(local_execution["response"]),
|
|
83
|
+
"",
|
|
84
|
+
"Use esse contexto apenas como apoio. A decisao, resposta final e revisao continuam sob responsabilidade do coordenador.",
|
|
85
|
+
]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def skipped(reason: str, message: str, *, model_plan: dict[str, Any]) -> dict[str, Any]:
|
|
90
|
+
return {
|
|
91
|
+
"kind": "local-llm-execution",
|
|
92
|
+
"agent_id": "local-llm-operator",
|
|
93
|
+
"capability_id": "delegate-operational-task",
|
|
94
|
+
"capability": "local-llm-operator.delegate-operational-task",
|
|
95
|
+
"status": "skipped",
|
|
96
|
+
"ok": False,
|
|
97
|
+
"reason": reason,
|
|
98
|
+
"message": message,
|
|
99
|
+
"model_provider": model_plan.get("local_llm_provider") or "ollama",
|
|
100
|
+
"requires_review": bool(model_plan.get("local_llm_recommended") or model_plan.get("local_llm_selected")),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def sanitize_llm_result(result: dict[str, Any]) -> dict[str, Any]:
|
|
105
|
+
return {
|
|
106
|
+
"status": result.get("status"),
|
|
107
|
+
"ok": result.get("ok"),
|
|
108
|
+
"llm_backend": result.get("llm_backend"),
|
|
109
|
+
"llm_backend_attempts": result.get("llm_backend_attempts"),
|
|
110
|
+
"message": result.get("message"),
|
|
111
|
+
"exit_code": result.get("exit_code"),
|
|
112
|
+
}
|
|
@@ -14,11 +14,14 @@ from cli.aikit import __version__
|
|
|
14
14
|
|
|
15
15
|
PROJECT_LOCK_NAME = "ai-devkit.lock"
|
|
16
16
|
GLOBAL_LOCK_NAME = "runtime.lock"
|
|
17
|
+
PROJECT_RUNTIME_DIR = ".ai-devkit"
|
|
18
|
+
GLOBAL_RUNTIME_DIR = ".agent-devkit"
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def lock_path(base: Path, scope: str) -> Path:
|
|
20
22
|
name = GLOBAL_LOCK_NAME if scope == "global" else PROJECT_LOCK_NAME
|
|
21
|
-
|
|
23
|
+
runtime_dir = GLOBAL_RUNTIME_DIR if scope == "global" else PROJECT_RUNTIME_DIR
|
|
24
|
+
return base / runtime_dir / name
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
def write_lock(
|