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
@@ -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 / ".ai-devkit"
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
- steps = ["Add `.ai-devkit/bin` to PATH when you want to call the installed `agent` directly."]
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.")
@@ -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
- return base / ".ai-devkit" / name
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(