agent-devkit 0.1.5 → 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 +2 -2
- package/package.json +1 -1
- package/runtime/README.md +74 -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 +76 -11
- 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/control_router.py +298 -0
- package/runtime/cli/aikit/decision_store.py +17 -0
- package/runtime/cli/aikit/diagnostics.py +2 -1
- package/runtime/cli/aikit/execution_reviewer.py +174 -0
- package/runtime/cli/aikit/install.py +4 -3
- package/runtime/cli/aikit/local_llm_operator.py +112 -0
- package/runtime/cli/aikit/lock.py +4 -1
- package/runtime/cli/aikit/main.py +355 -80
- package/runtime/cli/aikit/model_router.py +13 -4
- package/runtime/cli/aikit/orchestrator.py +535 -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
|
@@ -14,11 +14,16 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
from cli.aikit.aliases import add_alias, list_aliases, remove_alias, sync_aliases
|
|
16
16
|
from cli.aikit import __version__
|
|
17
|
+
from cli.aikit.agent_executor import execute_plan_tasks
|
|
18
|
+
from cli.aikit.app_home import app_home_status, migrate_default_home
|
|
17
19
|
from cli.aikit.audit import export_audit, list_audits, record_audit, show_audit
|
|
18
20
|
from cli.aikit.calendar import calendar_list, calendar_summary, calendar_today, calendar_tomorrow, configure_calendar
|
|
19
21
|
from cli.aikit.configuration_orchestrator import provider_wizard_from_requirement
|
|
20
|
-
from cli.aikit.
|
|
22
|
+
from cli.aikit.control_router import dispatch_natural_control_prompt as route_natural_control_prompt
|
|
23
|
+
from cli.aikit.control_router import plan_natural_control_prompt
|
|
24
|
+
from cli.aikit.decision_store import forget_decision, list_decisions, reset_decisions, set_decision
|
|
21
25
|
from cli.aikit.diagnostics import build_diagnostics
|
|
26
|
+
from cli.aikit.execution_reviewer import enforce_execution_review
|
|
22
27
|
from cli.aikit.fallback import evaluate_provider_requirements
|
|
23
28
|
from cli.aikit.github_pr import pr_create_automation, pr_inspect, pr_list_review_requests, pr_review
|
|
24
29
|
from cli.aikit.guardrails import evaluate_execution_guardrails
|
|
@@ -35,10 +40,17 @@ from cli.aikit.llm import (
|
|
|
35
40
|
from cli.aikit.memory import memory_path_payload, napkin_context, record_usage, reset_memory, show_memory
|
|
36
41
|
from cli.aikit.model_router import build_model_plan
|
|
37
42
|
from cli.aikit.ollama import ollama_models, ollama_pull, ollama_status, ollama_update
|
|
43
|
+
from cli.aikit.local_llm_operator import enrich_prompt_with_local_result, maybe_delegate_local_llm
|
|
44
|
+
from cli.aikit.orchestrator import (
|
|
45
|
+
attach_source_to_primary_task,
|
|
46
|
+
build_execution_plan,
|
|
47
|
+
mark_plan_after_execution,
|
|
48
|
+
mark_review_task,
|
|
49
|
+
)
|
|
38
50
|
from cli.aikit.personality import load_personality, reset_personality, setup_personality, update_personality
|
|
39
51
|
from cli.aikit.permissions import grant_permission, revoke_permission, show_permissions
|
|
40
52
|
from cli.aikit.provider_wizard import missing_source_wizard
|
|
41
|
-
from cli.aikit.review_gate import build_review_gate
|
|
53
|
+
from cli.aikit.review_gate import build_review_gate
|
|
42
54
|
from cli.aikit.sessions import (
|
|
43
55
|
build_contextual_prompt,
|
|
44
56
|
get_or_create_session,
|
|
@@ -85,6 +97,14 @@ from cli.aikit.sources import (
|
|
|
85
97
|
source_env,
|
|
86
98
|
source_status,
|
|
87
99
|
)
|
|
100
|
+
from cli.aikit.wizard_state import (
|
|
101
|
+
WizardStateError,
|
|
102
|
+
answer_wizard,
|
|
103
|
+
cancel_wizard,
|
|
104
|
+
create_provider_wizard,
|
|
105
|
+
list_wizards,
|
|
106
|
+
show_wizard,
|
|
107
|
+
)
|
|
88
108
|
|
|
89
109
|
|
|
90
110
|
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
|
|
@@ -122,6 +142,7 @@ DETERMINISTIC_COMMANDS = (
|
|
|
122
142
|
"decisions",
|
|
123
143
|
"ollama",
|
|
124
144
|
"install",
|
|
145
|
+
"wizard",
|
|
125
146
|
)
|
|
126
147
|
LLM_COMMANDS = ("agent",)
|
|
127
148
|
|
|
@@ -144,6 +165,8 @@ def main(argv: list[str] | None = None, *, prog: str | None = None) -> int:
|
|
|
144
165
|
|
|
145
166
|
if result is None:
|
|
146
167
|
return 0
|
|
168
|
+
if not getattr(args, "json", False):
|
|
169
|
+
result = maybe_run_interactive_wizard(result)
|
|
147
170
|
maybe_record_cli_audit(args, result=result, error=None)
|
|
148
171
|
|
|
149
172
|
if getattr(args, "json", False):
|
|
@@ -223,6 +246,14 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
223
246
|
source_parser.add_argument("--default-for-agent", action="append", default=[], help="agent id that should use this source by default")
|
|
224
247
|
source_parser.add_argument("--set-default", action="store_true", help="set as default source for its provider")
|
|
225
248
|
|
|
249
|
+
wizard_parser = subparsers.add_parser("wizard", help="continue agentic setup wizards")
|
|
250
|
+
wizard_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
251
|
+
wizard_parser.add_argument("action", nargs="?", default="list", choices=["list", "show", "answer", "cancel"])
|
|
252
|
+
wizard_parser.add_argument("wizard_id", nargs="?")
|
|
253
|
+
wizard_parser.add_argument("answer", nargs="*")
|
|
254
|
+
wizard_parser.add_argument("--status", help="filter wizard list by status")
|
|
255
|
+
wizard_parser.add_argument("--no-run", action="store_true", help="do not resume the original prompt after completing a wizard")
|
|
256
|
+
|
|
226
257
|
memory_parser = subparsers.add_parser("memory", help="inspect or reset local AI DevKit memory")
|
|
227
258
|
memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
228
259
|
memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset"])
|
|
@@ -320,7 +351,8 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
320
351
|
|
|
321
352
|
config_parser = subparsers.add_parser("config", help="inspect local Agent DevKit configuration")
|
|
322
353
|
config_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
323
|
-
config_parser.add_argument("action", nargs="?", default="show", choices=["show", "path"])
|
|
354
|
+
config_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "migrate-home"])
|
|
355
|
+
config_parser.add_argument("--dry-run", action="store_true", help="plan home migration without moving files")
|
|
324
356
|
|
|
325
357
|
for command_name, help_text in (
|
|
326
358
|
("tools", "manage enabled local tools"),
|
|
@@ -440,6 +472,8 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
|
|
|
440
472
|
return dispatch_credential(args)
|
|
441
473
|
if command == "source":
|
|
442
474
|
return dispatch_source(args)
|
|
475
|
+
if command == "wizard":
|
|
476
|
+
return dispatch_wizard(args)
|
|
443
477
|
if command == "memory":
|
|
444
478
|
return dispatch_memory(args)
|
|
445
479
|
if command == "personality":
|
|
@@ -607,12 +641,15 @@ def dispatch_config(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
607
641
|
from cli.aikit.llm import config_path
|
|
608
642
|
|
|
609
643
|
if args.action == "path":
|
|
610
|
-
return {"kind": "config", "status": "ok", "path": str(config_path())}
|
|
644
|
+
return {"kind": "config", "status": "ok", "path": str(config_path()), "home": app_home_status()}
|
|
645
|
+
if args.action == "migrate-home":
|
|
646
|
+
return migrate_default_home(dry_run=effective_dry_run(args))
|
|
611
647
|
if args.action == "show":
|
|
612
648
|
return {
|
|
613
649
|
"kind": "config",
|
|
614
650
|
"status": "ok",
|
|
615
651
|
"path": str(config_path()),
|
|
652
|
+
"home": app_home_status(),
|
|
616
653
|
"decisions": list_decisions(),
|
|
617
654
|
"llm": llm_preference(),
|
|
618
655
|
"ollama": ollama_status(),
|
|
@@ -655,7 +692,7 @@ def dispatch_decisions(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
655
692
|
if not args.item_id:
|
|
656
693
|
raise DevKitError("decisions forget requires an item id")
|
|
657
694
|
category = args.category or "tools"
|
|
658
|
-
return
|
|
695
|
+
return forget_decision(category, args.item_id)
|
|
659
696
|
except ValueError as exc:
|
|
660
697
|
raise DevKitError(str(exc)) from exc
|
|
661
698
|
raise DevKitError(f"unsupported decisions action: {args.action}")
|
|
@@ -776,6 +813,117 @@ def dispatch_source(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
776
813
|
raise DevKitError(f"unsupported source action: {args.action}")
|
|
777
814
|
|
|
778
815
|
|
|
816
|
+
def dispatch_wizard(args: argparse.Namespace) -> dict[str, Any]:
|
|
817
|
+
try:
|
|
818
|
+
if args.action == "list":
|
|
819
|
+
return list_wizards(status=args.status)
|
|
820
|
+
if args.action == "show":
|
|
821
|
+
require_id(args.wizard_id, "wizard show")
|
|
822
|
+
return show_wizard(args.wizard_id)
|
|
823
|
+
if args.action == "cancel":
|
|
824
|
+
require_id(args.wizard_id, "wizard cancel")
|
|
825
|
+
return cancel_wizard(args.wizard_id)
|
|
826
|
+
if args.action == "answer":
|
|
827
|
+
require_id(args.wizard_id, "wizard answer")
|
|
828
|
+
answer = " ".join(args.answer or []).strip()
|
|
829
|
+
if not answer:
|
|
830
|
+
raise DevKitError("wizard answer requires an answer")
|
|
831
|
+
payload = answer_wizard(args.wizard_id, answer)
|
|
832
|
+
if payload.get("status") == "completed" and not args.no_run:
|
|
833
|
+
resume_prompt = payload.get("resume_prompt") or (payload.get("wizard") or {}).get("resume_prompt")
|
|
834
|
+
if resume_prompt:
|
|
835
|
+
payload["resumed_prompt"] = True
|
|
836
|
+
payload["resume_result"] = resume_agent_prompt(str(resume_prompt))
|
|
837
|
+
else:
|
|
838
|
+
payload["resumed_prompt"] = False
|
|
839
|
+
else:
|
|
840
|
+
payload.setdefault("resumed_prompt", False)
|
|
841
|
+
return payload
|
|
842
|
+
except WizardStateError as exc:
|
|
843
|
+
raise DevKitError(str(exc)) from exc
|
|
844
|
+
except SourceRegistryError as exc:
|
|
845
|
+
raise DevKitError(str(exc)) from exc
|
|
846
|
+
raise DevKitError(f"unsupported wizard action: {args.action}")
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def maybe_run_interactive_wizard(result: dict[str, Any]) -> dict[str, Any]:
|
|
850
|
+
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
851
|
+
return result
|
|
852
|
+
wizard = result.get("setup_wizard") if isinstance(result.get("setup_wizard"), dict) else None
|
|
853
|
+
if not wizard or not wizard.get("wizard_id") or result.get("status") != "needs-input":
|
|
854
|
+
return result
|
|
855
|
+
print(result.get("message") or "O agente precisa de configuracao antes de continuar.")
|
|
856
|
+
return run_interactive_wizard(str(wizard["wizard_id"]))
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def run_interactive_wizard(wizard_id: str) -> dict[str, Any]:
|
|
860
|
+
payload = show_wizard(wizard_id)
|
|
861
|
+
while True:
|
|
862
|
+
wizard = payload.get("wizard") if isinstance(payload.get("wizard"), dict) else {}
|
|
863
|
+
question = payload.get("next_question") or wizard.get("next_question")
|
|
864
|
+
if not isinstance(question, dict):
|
|
865
|
+
return payload
|
|
866
|
+
print_interactive_question(question)
|
|
867
|
+
try:
|
|
868
|
+
answer = input("> ")
|
|
869
|
+
except (EOFError, KeyboardInterrupt):
|
|
870
|
+
print()
|
|
871
|
+
return cancel_wizard(wizard_id, reason="interactive wizard interrupted")
|
|
872
|
+
if answer.strip().lower() in {"cancelar", "cancel", "sair", "exit"}:
|
|
873
|
+
return cancel_wizard(wizard_id, reason="interactive wizard cancelled by user")
|
|
874
|
+
try:
|
|
875
|
+
payload = answer_wizard(wizard_id, answer)
|
|
876
|
+
except WizardStateError as exc:
|
|
877
|
+
print(f"Erro: {exc}")
|
|
878
|
+
continue
|
|
879
|
+
if payload.get("status") == "completed":
|
|
880
|
+
resume_prompt = payload.get("resume_prompt") or (payload.get("wizard") or {}).get("resume_prompt")
|
|
881
|
+
if resume_prompt:
|
|
882
|
+
payload["resumed_prompt"] = True
|
|
883
|
+
payload["resume_result"] = resume_agent_prompt(str(resume_prompt))
|
|
884
|
+
else:
|
|
885
|
+
payload["resumed_prompt"] = False
|
|
886
|
+
return payload
|
|
887
|
+
if payload.get("status") in {"cancelled", "denied-by-user", "failed"}:
|
|
888
|
+
payload.setdefault("resumed_prompt", False)
|
|
889
|
+
return payload
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def print_interactive_question(question: dict[str, Any]) -> None:
|
|
893
|
+
text = question.get("text") or "Informe a resposta."
|
|
894
|
+
print(f"\nPergunta: {text}")
|
|
895
|
+
if question.get("type") == "confirm":
|
|
896
|
+
print("[s/N]")
|
|
897
|
+
options = question.get("options")
|
|
898
|
+
if isinstance(options, list) and options:
|
|
899
|
+
print("Opcoes: " + ", ".join(str(item) for item in options))
|
|
900
|
+
if question.get("suggested_value"):
|
|
901
|
+
print(f"Sugestao: {question['suggested_value']}")
|
|
902
|
+
if question.get("env_ref_key"):
|
|
903
|
+
print("Informe o nome da variavel de ambiente, nao o valor da credencial.")
|
|
904
|
+
print("Digite 'cancelar' para interromper.")
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def resume_agent_prompt(prompt: str) -> dict[str, Any]:
|
|
908
|
+
args = argparse.Namespace(
|
|
909
|
+
command="agent",
|
|
910
|
+
prompt=prompt.split(),
|
|
911
|
+
session_id=None,
|
|
912
|
+
new_session=False,
|
|
913
|
+
llm=None,
|
|
914
|
+
no_llm_fallback=False,
|
|
915
|
+
dry_run=False,
|
|
916
|
+
global_dry_run=False,
|
|
917
|
+
prog_name="agent",
|
|
918
|
+
json=True,
|
|
919
|
+
version=False,
|
|
920
|
+
sessions_shortcut=False,
|
|
921
|
+
)
|
|
922
|
+
result = agent_requires_llm(args)
|
|
923
|
+
result.pop("audit", None)
|
|
924
|
+
return result
|
|
925
|
+
|
|
926
|
+
|
|
779
927
|
def dispatch_memory(args: argparse.Namespace) -> dict[str, Any]:
|
|
780
928
|
if args.action == "show":
|
|
781
929
|
return show_memory(ROOT, agent_id=args.agent_id, source_id=args.source_id)
|
|
@@ -1093,29 +1241,124 @@ def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
1093
1241
|
return finalize_agent_session(natural_result, session, prompt, backend=args.llm)
|
|
1094
1242
|
route = route_prompt(prompt)
|
|
1095
1243
|
if route:
|
|
1096
|
-
result =
|
|
1244
|
+
result = invoke_agentic_route(prompt, route)
|
|
1097
1245
|
return finalize_agent_session(result, session, prompt, backend=args.llm)
|
|
1098
1246
|
contextual_prompt = build_contextual_prompt(str(session["id"]), prompt)
|
|
1099
|
-
|
|
1247
|
+
execution_plan = build_execution_plan(ROOT, prompt, dry_run=False)
|
|
1248
|
+
if execution_plan.get("configuration_tasks"):
|
|
1249
|
+
result = agentic_needs_input_from_plan(prompt, execution_plan)
|
|
1250
|
+
return finalize_agent_session(result, session, prompt, backend=args.llm)
|
|
1251
|
+
model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt)
|
|
1252
|
+
review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, model_plan=model_plan)
|
|
1253
|
+
local_llm_execution = maybe_delegate_local_llm(prompt, model_plan)
|
|
1254
|
+
coordinator_prompt = enrich_prompt_with_local_result(contextual_prompt, local_llm_execution)
|
|
1100
1255
|
result = invoke_agent_prompt(
|
|
1101
|
-
|
|
1256
|
+
coordinator_prompt,
|
|
1102
1257
|
args.llm,
|
|
1103
1258
|
public_name=name,
|
|
1104
1259
|
allow_fallback=not args.no_llm_fallback,
|
|
1105
1260
|
)
|
|
1106
|
-
|
|
1261
|
+
review_result = {
|
|
1262
|
+
"kind": "execution-review",
|
|
1263
|
+
"agent_id": "execution-reviewer",
|
|
1264
|
+
"capability_id": "review-final-output",
|
|
1265
|
+
"status": "not-run",
|
|
1266
|
+
"ok": False,
|
|
1267
|
+
}
|
|
1107
1268
|
if result.get("ok"):
|
|
1108
|
-
|
|
1269
|
+
result, review_gate, review_result = enforce_execution_review(
|
|
1270
|
+
prompt=prompt,
|
|
1271
|
+
result=result,
|
|
1272
|
+
review_gate=review_gate,
|
|
1273
|
+
execution_plan=execution_plan,
|
|
1274
|
+
producer_backend=str(result.get("llm_backend") or args.llm or ""),
|
|
1275
|
+
)
|
|
1276
|
+
if review_result.get("ok"):
|
|
1277
|
+
execution_plan = mark_review_task(execution_plan, reviewer=str(review_result.get("llm_backend") or "execution-reviewer"))
|
|
1278
|
+
elif review_gate.get("status") == "needs-review":
|
|
1279
|
+
execution_plan = mark_review_task_needs_review(execution_plan, review_result)
|
|
1109
1280
|
result["model_plan"] = model_plan
|
|
1281
|
+
result["local_llm_execution"] = local_llm_execution
|
|
1110
1282
|
result["review_gate"] = review_gate
|
|
1283
|
+
result["review_result"] = review_result
|
|
1284
|
+
result["review_task"] = execution_plan.get("review_task")
|
|
1285
|
+
result["execution_plan"] = execution_plan
|
|
1286
|
+
result["orchestration_trace"] = execution_plan.get("trace", [])
|
|
1111
1287
|
result["prompt_length"] = len(prompt)
|
|
1112
1288
|
result["session_context_applied"] = contextual_prompt != prompt
|
|
1289
|
+
result["local_context_applied"] = coordinator_prompt != contextual_prompt
|
|
1113
1290
|
if result.get("response"):
|
|
1114
1291
|
result["response"] = enforce_identity_response(str(result["response"]), prompt, name=name)
|
|
1115
1292
|
result["identity"] = {"name": name, "source": "local"}
|
|
1116
1293
|
return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or args.llm)
|
|
1117
1294
|
|
|
1118
1295
|
|
|
1296
|
+
def mark_review_task_needs_review(execution_plan: dict[str, Any], review_result: dict[str, Any]) -> dict[str, Any]:
|
|
1297
|
+
task = dict(execution_plan.get("review_task") or {})
|
|
1298
|
+
if task:
|
|
1299
|
+
task["status"] = "needs-review"
|
|
1300
|
+
task["reviewer"] = None
|
|
1301
|
+
task["message"] = review_result.get("message")
|
|
1302
|
+
execution_plan["review_task"] = task
|
|
1303
|
+
return execution_plan
|
|
1304
|
+
|
|
1305
|
+
|
|
1306
|
+
def agentic_needs_input_from_plan(prompt: str, execution_plan: dict[str, Any]) -> dict[str, Any]:
|
|
1307
|
+
configuration_task = next(
|
|
1308
|
+
(task for task in execution_plan.get("configuration_tasks") or [] if isinstance(task, dict)),
|
|
1309
|
+
{},
|
|
1310
|
+
)
|
|
1311
|
+
wizard = configuration_task.get("setup_wizard") if isinstance(configuration_task.get("setup_wizard"), dict) else {}
|
|
1312
|
+
provider = configuration_task.get("provider") or wizard.get("provider")
|
|
1313
|
+
payload = {
|
|
1314
|
+
"kind": "agent",
|
|
1315
|
+
"status": "needs-input",
|
|
1316
|
+
"ok": False,
|
|
1317
|
+
"mode": "agentic-plan",
|
|
1318
|
+
"prompt_received": True,
|
|
1319
|
+
"prompt_length": len(prompt),
|
|
1320
|
+
"provider": provider,
|
|
1321
|
+
"source_provider": provider,
|
|
1322
|
+
"requires_source": True,
|
|
1323
|
+
"execution_plan": execution_plan,
|
|
1324
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1325
|
+
"setup_wizard": wizard,
|
|
1326
|
+
"next_question": wizard.get("next_question"),
|
|
1327
|
+
"message": wizard.get("message") or "O plano multiagente precisa de configuracao antes de executar.",
|
|
1328
|
+
"next_steps": [
|
|
1329
|
+
"Responda a pergunta do wizard para autorizar ou negar a configuracao deste provider.",
|
|
1330
|
+
"Depois de configurar a fonte, reexecute ou retome o mesmo prompt.",
|
|
1331
|
+
],
|
|
1332
|
+
"exit_code": 2,
|
|
1333
|
+
}
|
|
1334
|
+
return persist_setup_wizard_payload(payload, execution_plan=execution_plan)
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
def persist_setup_wizard_payload(
|
|
1338
|
+
payload: dict[str, Any],
|
|
1339
|
+
*,
|
|
1340
|
+
execution_plan: dict[str, Any] | None = None,
|
|
1341
|
+
route: dict[str, Any] | None = None,
|
|
1342
|
+
) -> dict[str, Any]:
|
|
1343
|
+
wizard = payload.get("setup_wizard")
|
|
1344
|
+
if not isinstance(wizard, dict) or wizard.get("wizard_id"):
|
|
1345
|
+
return payload
|
|
1346
|
+
persisted = create_provider_wizard(
|
|
1347
|
+
wizard,
|
|
1348
|
+
execution_plan=execution_plan or payload.get("execution_plan"),
|
|
1349
|
+
route=route or payload.get("route"),
|
|
1350
|
+
)
|
|
1351
|
+
payload["setup_wizard"] = persisted
|
|
1352
|
+
payload["next_question"] = persisted.get("next_question")
|
|
1353
|
+
if execution_plan and execution_plan.get("configuration_tasks"):
|
|
1354
|
+
for task in execution_plan.get("configuration_tasks") or []:
|
|
1355
|
+
if isinstance(task, dict) and task.get("provider") == persisted.get("provider"):
|
|
1356
|
+
task["setup_wizard"] = persisted
|
|
1357
|
+
break
|
|
1358
|
+
payload.setdefault("wizard_id", persisted.get("wizard_id"))
|
|
1359
|
+
return payload
|
|
1360
|
+
|
|
1361
|
+
|
|
1119
1362
|
def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
|
|
1120
1363
|
normalized = " ".join(prompt.lower().split())
|
|
1121
1364
|
control_result = dispatch_natural_control_prompt(normalized)
|
|
@@ -1171,68 +1414,20 @@ def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
|
|
|
1171
1414
|
|
|
1172
1415
|
|
|
1173
1416
|
def dispatch_natural_control_prompt(normalized_prompt: str) -> dict[str, Any] | None:
|
|
1174
|
-
|
|
1175
|
-
return {
|
|
1176
|
-
"kind": "agent",
|
|
1177
|
-
"status": "ok",
|
|
1178
|
-
"ok": True,
|
|
1179
|
-
"mode": "control-center-route",
|
|
1180
|
-
"requires_llm": False,
|
|
1181
|
-
"response": "Estas sao as decisoes locais registradas.",
|
|
1182
|
-
"result": list_decisions(),
|
|
1183
|
-
}
|
|
1184
|
-
control_targets = {
|
|
1185
|
-
"ollama": ("tools", "ollama"),
|
|
1186
|
-
"azure devops": ("tools", "azure-devops"),
|
|
1187
|
-
"azure": ("tools", "azure-devops"),
|
|
1188
|
-
"github": ("integrations", "github"),
|
|
1189
|
-
"figma": ("tools", "figma"),
|
|
1190
|
-
}
|
|
1191
|
-
action: str | None = None
|
|
1192
|
-
if any(marker in normalized_prompt for marker in ("desative", "desabilite", "disable", "bloqueie")):
|
|
1193
|
-
action = "disable"
|
|
1194
|
-
if any(marker in normalized_prompt for marker in ("reative", "ative", "habilite", "enable")):
|
|
1195
|
-
action = "enable"
|
|
1196
|
-
if not action:
|
|
1197
|
-
return None
|
|
1198
|
-
for marker, (category, item_id) in control_targets.items():
|
|
1199
|
-
if marker in normalized_prompt:
|
|
1200
|
-
state = "enabled" if action == "enable" else "disabled_by_user"
|
|
1201
|
-
result = set_decision(category, item_id, state, reason=f"natural prompt: {action}")
|
|
1202
|
-
return {
|
|
1203
|
-
"kind": "agent",
|
|
1204
|
-
"status": "ok",
|
|
1205
|
-
"ok": True,
|
|
1206
|
-
"mode": "control-center-route",
|
|
1207
|
-
"requires_llm": False,
|
|
1208
|
-
"response": f"{item_id} foi {'ativado' if state == 'enabled' else 'desativado'} para {category}.",
|
|
1209
|
-
"result": {
|
|
1210
|
-
"category": category,
|
|
1211
|
-
"id": item_id,
|
|
1212
|
-
"state": state,
|
|
1213
|
-
"decision": result.get("item"),
|
|
1214
|
-
},
|
|
1215
|
-
}
|
|
1216
|
-
if any(marker in normalized_prompt for marker in ("ferramentas", "tools")) and any(
|
|
1217
|
-
marker in normalized_prompt for marker in ("mostre", "liste", "ver", "mostrar", "listar")
|
|
1218
|
-
):
|
|
1219
|
-
return {
|
|
1220
|
-
"kind": "agent",
|
|
1221
|
-
"status": "ok",
|
|
1222
|
-
"ok": True,
|
|
1223
|
-
"mode": "control-center-route",
|
|
1224
|
-
"requires_llm": False,
|
|
1225
|
-
"response": "Estas sao as ferramentas com decisoes locais registradas.",
|
|
1226
|
-
"result": list_decisions("tools"),
|
|
1227
|
-
}
|
|
1228
|
-
return None
|
|
1417
|
+
return route_natural_control_prompt(ROOT, normalized_prompt)
|
|
1229
1418
|
|
|
1230
1419
|
|
|
1231
1420
|
def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str, Any]:
|
|
1232
1421
|
normalized = " ".join(prompt.lower().split())
|
|
1422
|
+
control_plan = plan_natural_control_prompt(ROOT, normalized)
|
|
1423
|
+
if control_plan:
|
|
1424
|
+
control_plan["prompt_received"] = True
|
|
1425
|
+
control_plan["prompt_length"] = len(prompt)
|
|
1426
|
+
return control_plan
|
|
1233
1427
|
route = route_prompt(prompt)
|
|
1234
|
-
|
|
1235
|
-
|
|
1428
|
+
execution_plan = build_execution_plan(ROOT, prompt, dry_run=True)
|
|
1429
|
+
model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt, route=route)
|
|
1430
|
+
review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, route=route, model_plan=model_plan)
|
|
1236
1431
|
plan: dict[str, Any] = {
|
|
1237
1432
|
"kind": "agent",
|
|
1238
1433
|
"status": "planned",
|
|
@@ -1251,6 +1446,8 @@ def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str,
|
|
|
1251
1446
|
"permissions": [],
|
|
1252
1447
|
"model_plan": model_plan,
|
|
1253
1448
|
"review_gate": review_gate,
|
|
1449
|
+
"execution_plan": execution_plan,
|
|
1450
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1254
1451
|
"response": "Dry-run: nenhuma chamada LLM ou escrita externa foi executada.",
|
|
1255
1452
|
}
|
|
1256
1453
|
if "agenda" in normalized:
|
|
@@ -1324,7 +1521,8 @@ def finalize_agent_session(
|
|
|
1324
1521
|
return result
|
|
1325
1522
|
|
|
1326
1523
|
|
|
1327
|
-
def
|
|
1524
|
+
def invoke_agentic_route(prompt: str, route: dict[str, Any]) -> dict[str, Any]:
|
|
1525
|
+
execution_plan = build_execution_plan(ROOT, prompt, dry_run=False)
|
|
1328
1526
|
try:
|
|
1329
1527
|
source = resolve_source(
|
|
1330
1528
|
provider=route.get("provider"),
|
|
@@ -1336,7 +1534,14 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
|
|
|
1336
1534
|
|
|
1337
1535
|
if not source:
|
|
1338
1536
|
wizard = missing_source_wizard(prompt, route, root=ROOT)
|
|
1339
|
-
|
|
1537
|
+
if execution_plan.get("configuration_tasks"):
|
|
1538
|
+
execution_plan["configuration_tasks"][0]["setup_wizard"] = wizard
|
|
1539
|
+
execution_plan["status"] = "needs-input"
|
|
1540
|
+
execution_plan["trace"] = [
|
|
1541
|
+
{"agent_id": "task-orchestrator", "action": "plan", "status": "needs-input"},
|
|
1542
|
+
{"agent_id": "provider-configurator", "action": "configure", "status": "waiting-for-user"},
|
|
1543
|
+
]
|
|
1544
|
+
payload = {
|
|
1340
1545
|
"kind": "agent",
|
|
1341
1546
|
"status": "needs-input",
|
|
1342
1547
|
"ok": False,
|
|
@@ -1347,6 +1552,8 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
|
|
|
1347
1552
|
"prompt_length": len(prompt),
|
|
1348
1553
|
"route": route,
|
|
1349
1554
|
"napkin": napkin_context(ROOT, agent_id=route.get("agent_id")),
|
|
1555
|
+
"execution_plan": execution_plan,
|
|
1556
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1350
1557
|
"setup_wizard": wizard,
|
|
1351
1558
|
"next_question": wizard.get("next_question"),
|
|
1352
1559
|
"message": wizard.get("message"),
|
|
@@ -1357,21 +1564,51 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
|
|
|
1357
1564
|
],
|
|
1358
1565
|
"exit_code": 2,
|
|
1359
1566
|
}
|
|
1567
|
+
return persist_setup_wizard_payload(payload, execution_plan=execution_plan, route=route)
|
|
1360
1568
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1569
|
+
execution_plan = attach_source_to_primary_task(execution_plan)
|
|
1570
|
+
executed, blocked = execute_plan_tasks(
|
|
1571
|
+
execution_plan,
|
|
1572
|
+
load_agent=load_agent,
|
|
1573
|
+
run_capability=lambda agent, capability_id, capability_args: run_capability(agent, capability_id, capability_args, capture_output=True),
|
|
1574
|
+
)
|
|
1575
|
+
execution_plan = mark_plan_after_execution(execution_plan, executed, blocked)
|
|
1576
|
+
result = (executed[0].get("result") if executed else blocked[0].get("result") if blocked and isinstance(blocked[0].get("result"), dict) else {}) or {}
|
|
1364
1577
|
response = result.get("stdout") or result.get("error") or ""
|
|
1365
1578
|
record_usage(prompt, route=route, source_id=str(source["id"]))
|
|
1366
|
-
model_plan = build_model_plan(prompt, route=route)
|
|
1367
|
-
review_gate = build_review_gate(prompt, route=route, model_plan=model_plan)
|
|
1579
|
+
model_plan = execution_plan.get("model_plan") if isinstance(execution_plan.get("model_plan"), dict) else build_model_plan(prompt, route=route)
|
|
1580
|
+
review_gate = execution_plan.get("review_gate") if isinstance(execution_plan.get("review_gate"), dict) else build_review_gate(prompt, route=route, model_plan=model_plan)
|
|
1581
|
+
review_payload = {
|
|
1582
|
+
"kind": "agent",
|
|
1583
|
+
"status": execution_plan.get("status") if execution_plan.get("status") != "partial" else result.get("status"),
|
|
1584
|
+
"ok": bool(executed) and not blocked,
|
|
1585
|
+
"response": response,
|
|
1586
|
+
}
|
|
1587
|
+
review_result = {
|
|
1588
|
+
"kind": "execution-review",
|
|
1589
|
+
"agent_id": "execution-reviewer",
|
|
1590
|
+
"capability_id": "review-final-output",
|
|
1591
|
+
"status": "not-run",
|
|
1592
|
+
"ok": False,
|
|
1593
|
+
}
|
|
1368
1594
|
if result.get("ok"):
|
|
1369
|
-
review_gate =
|
|
1595
|
+
review_payload, review_gate, review_result = enforce_execution_review(
|
|
1596
|
+
prompt=prompt,
|
|
1597
|
+
result=review_payload,
|
|
1598
|
+
review_gate=review_gate,
|
|
1599
|
+
execution_plan=execution_plan,
|
|
1600
|
+
producer_backend="deterministic-capability",
|
|
1601
|
+
)
|
|
1602
|
+
if review_result.get("ok"):
|
|
1603
|
+
execution_plan = mark_review_task(execution_plan, reviewer=str(review_result.get("llm_backend") or "execution-reviewer"))
|
|
1604
|
+
elif review_gate.get("status") == "needs-review":
|
|
1605
|
+
execution_plan = mark_review_task_needs_review(execution_plan, review_result)
|
|
1370
1606
|
return {
|
|
1371
1607
|
"kind": "agent",
|
|
1372
|
-
"status":
|
|
1373
|
-
"ok":
|
|
1374
|
-
"mode": "
|
|
1608
|
+
"status": review_payload.get("status"),
|
|
1609
|
+
"ok": bool(review_payload.get("ok")),
|
|
1610
|
+
"mode": "agentic-route",
|
|
1611
|
+
"legacy_mode": "deterministic-route",
|
|
1375
1612
|
"prompt_received": True,
|
|
1376
1613
|
"prompt_length": len(prompt),
|
|
1377
1614
|
"route": route,
|
|
@@ -1379,9 +1616,13 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
|
|
|
1379
1616
|
"napkin": napkin_context(ROOT, agent_id=route.get("agent_id"), source_id=str(source["id"])),
|
|
1380
1617
|
"model_plan": model_plan,
|
|
1381
1618
|
"review_gate": review_gate,
|
|
1382
|
-
"
|
|
1619
|
+
"review_result": review_result,
|
|
1620
|
+
"review_task": execution_plan.get("review_task"),
|
|
1621
|
+
"execution_plan": execution_plan,
|
|
1622
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1623
|
+
"response": review_payload.get("response") or response,
|
|
1383
1624
|
"result": result,
|
|
1384
|
-
"exit_code":
|
|
1625
|
+
"exit_code": review_payload.get("exit_code", result.get("exit_code", 0 if executed and not blocked else 1)),
|
|
1385
1626
|
}
|
|
1386
1627
|
|
|
1387
1628
|
|
|
@@ -1601,6 +1842,7 @@ def run_capability(
|
|
|
1601
1842
|
"Informe uma referencia segura de credencial por variavel de ambiente, arquivo ou cadeia nativa quando solicitado.",
|
|
1602
1843
|
"Reexecute ou retome a mesma capability depois que a configuracao estiver salva.",
|
|
1603
1844
|
]
|
|
1845
|
+
payload = persist_setup_wizard_payload(payload)
|
|
1604
1846
|
return payload
|
|
1605
1847
|
|
|
1606
1848
|
runner_ref = (data.get("entrypoint", {}) or {}).get("runner")
|
|
@@ -1949,6 +2191,8 @@ def print_human(result: dict[str, Any]) -> None:
|
|
|
1949
2191
|
print_source_configure(result)
|
|
1950
2192
|
elif kind == "source-remove":
|
|
1951
2193
|
print_source_remove(result)
|
|
2194
|
+
elif kind in {"wizards", "wizard"}:
|
|
2195
|
+
print_wizard(result)
|
|
1952
2196
|
elif kind == "memory":
|
|
1953
2197
|
print_memory(result)
|
|
1954
2198
|
elif kind == "memory-path":
|
|
@@ -2131,6 +2375,10 @@ def print_agent_response(result: dict[str, Any]) -> None:
|
|
|
2131
2375
|
print(f"\nPergunta: {question['text']}")
|
|
2132
2376
|
if question.get("type") == "confirm":
|
|
2133
2377
|
print("[s/N]")
|
|
2378
|
+
wizard = result.get("setup_wizard") if isinstance(result.get("setup_wizard"), dict) else {}
|
|
2379
|
+
if wizard.get("wizard_id"):
|
|
2380
|
+
print(f"\nWizard: {wizard['wizard_id']}")
|
|
2381
|
+
print(f"Responder: agent wizard answer {wizard['wizard_id']} <resposta>")
|
|
2134
2382
|
if result.get("llm_backend"):
|
|
2135
2383
|
print(f"Requested backend: {result['llm_backend']}")
|
|
2136
2384
|
if result.get("next_steps"):
|
|
@@ -2139,6 +2387,33 @@ def print_agent_response(result: dict[str, Any]) -> None:
|
|
|
2139
2387
|
print(f"- {step}")
|
|
2140
2388
|
|
|
2141
2389
|
|
|
2390
|
+
def print_wizard(result: dict[str, Any]) -> None:
|
|
2391
|
+
if result.get("kind") == "wizards":
|
|
2392
|
+
items = result.get("items") or []
|
|
2393
|
+
print(f"Wizards: {len(items)}")
|
|
2394
|
+
for item in items:
|
|
2395
|
+
print(f"- {item.get('wizard_id')} {item.get('status')} {item.get('provider')}")
|
|
2396
|
+
return
|
|
2397
|
+
wizard = result.get("wizard") if isinstance(result.get("wizard"), dict) else result.get("setup_wizard")
|
|
2398
|
+
if not isinstance(wizard, dict):
|
|
2399
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
2400
|
+
return
|
|
2401
|
+
print(f"Wizard {wizard.get('wizard_id')}: {wizard.get('status')}")
|
|
2402
|
+
print(f"Provider: {wizard.get('provider')}")
|
|
2403
|
+
question = result.get("next_question") or wizard.get("next_question")
|
|
2404
|
+
if isinstance(question, dict) and question.get("text"):
|
|
2405
|
+
print(f"Pergunta: {question['text']}")
|
|
2406
|
+
if question.get("type") == "confirm":
|
|
2407
|
+
print("[s/N]")
|
|
2408
|
+
print(f"Responder: agent wizard answer {wizard.get('wizard_id')} <resposta>")
|
|
2409
|
+
if result.get("source_result"):
|
|
2410
|
+
source = result["source_result"].get("source") or {}
|
|
2411
|
+
print(f"Source configurada: {source.get('id')}")
|
|
2412
|
+
if result.get("resumed_prompt"):
|
|
2413
|
+
resume = result.get("resume_result") or {}
|
|
2414
|
+
print(f"Prompt retomado: {resume.get('status')}")
|
|
2415
|
+
|
|
2416
|
+
|
|
2142
2417
|
def print_sources(result: dict[str, Any]) -> None:
|
|
2143
2418
|
print(f"Sources config: {result['config_path']}")
|
|
2144
2419
|
if not result["items"]:
|