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
|
@@ -14,9 +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
|
|
21
|
+
from cli.aikit.configuration_orchestrator import provider_wizard_from_requirement
|
|
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
|
|
19
25
|
from cli.aikit.diagnostics import build_diagnostics
|
|
26
|
+
from cli.aikit.execution_reviewer import enforce_execution_review
|
|
20
27
|
from cli.aikit.fallback import evaluate_provider_requirements
|
|
21
28
|
from cli.aikit.github_pr import pr_create_automation, pr_inspect, pr_list_review_requests, pr_review
|
|
22
29
|
from cli.aikit.guardrails import evaluate_execution_guardrails
|
|
@@ -31,8 +38,19 @@ from cli.aikit.llm import (
|
|
|
31
38
|
set_llm_preference,
|
|
32
39
|
)
|
|
33
40
|
from cli.aikit.memory import memory_path_payload, napkin_context, record_usage, reset_memory, show_memory
|
|
41
|
+
from cli.aikit.model_router import build_model_plan
|
|
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
|
+
)
|
|
34
50
|
from cli.aikit.personality import load_personality, reset_personality, setup_personality, update_personality
|
|
35
51
|
from cli.aikit.permissions import grant_permission, revoke_permission, show_permissions
|
|
52
|
+
from cli.aikit.provider_wizard import missing_source_wizard
|
|
53
|
+
from cli.aikit.review_gate import build_review_gate
|
|
36
54
|
from cli.aikit.sessions import (
|
|
37
55
|
build_contextual_prompt,
|
|
38
56
|
get_or_create_session,
|
|
@@ -79,6 +97,14 @@ from cli.aikit.sources import (
|
|
|
79
97
|
source_env,
|
|
80
98
|
source_status,
|
|
81
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
|
+
)
|
|
82
108
|
|
|
83
109
|
|
|
84
110
|
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
|
|
@@ -109,7 +135,14 @@ DETERMINISTIC_COMMANDS = (
|
|
|
109
135
|
"pr",
|
|
110
136
|
"permissions",
|
|
111
137
|
"audit",
|
|
138
|
+
"config",
|
|
139
|
+
"tools",
|
|
140
|
+
"integrations",
|
|
141
|
+
"skills",
|
|
142
|
+
"decisions",
|
|
143
|
+
"ollama",
|
|
112
144
|
"install",
|
|
145
|
+
"wizard",
|
|
113
146
|
)
|
|
114
147
|
LLM_COMMANDS = ("agent",)
|
|
115
148
|
|
|
@@ -132,6 +165,8 @@ def main(argv: list[str] | None = None, *, prog: str | None = None) -> int:
|
|
|
132
165
|
|
|
133
166
|
if result is None:
|
|
134
167
|
return 0
|
|
168
|
+
if not getattr(args, "json", False):
|
|
169
|
+
result = maybe_run_interactive_wizard(result)
|
|
135
170
|
maybe_record_cli_audit(args, result=result, error=None)
|
|
136
171
|
|
|
137
172
|
if getattr(args, "json", False):
|
|
@@ -211,6 +246,14 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
211
246
|
source_parser.add_argument("--default-for-agent", action="append", default=[], help="agent id that should use this source by default")
|
|
212
247
|
source_parser.add_argument("--set-default", action="store_true", help="set as default source for its provider")
|
|
213
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
|
+
|
|
214
257
|
memory_parser = subparsers.add_parser("memory", help="inspect or reset local AI DevKit memory")
|
|
215
258
|
memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
216
259
|
memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset"])
|
|
@@ -306,13 +349,41 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|
|
306
349
|
audit_parser.add_argument("--limit", type=int, default=20)
|
|
307
350
|
audit_parser.add_argument("--format", default="md", choices=["md", "json"])
|
|
308
351
|
|
|
352
|
+
config_parser = subparsers.add_parser("config", help="inspect local Agent DevKit configuration")
|
|
353
|
+
config_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
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")
|
|
356
|
+
|
|
357
|
+
for command_name, help_text in (
|
|
358
|
+
("tools", "manage enabled local tools"),
|
|
359
|
+
("integrations", "manage provider integration decisions"),
|
|
360
|
+
("skills", "manage local skill decisions"),
|
|
361
|
+
):
|
|
362
|
+
control_parser = subparsers.add_parser(command_name, help=help_text)
|
|
363
|
+
control_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
364
|
+
control_parser.add_argument("action", nargs="?", default="list", choices=["list", "enable", "disable"])
|
|
365
|
+
control_parser.add_argument("item_id", nargs="?")
|
|
366
|
+
|
|
367
|
+
decisions_parser = subparsers.add_parser("decisions", help="inspect or reset local opt-in and opt-out decisions")
|
|
368
|
+
decisions_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
369
|
+
decisions_parser.add_argument("action", nargs="?", default="list", choices=["list", "forget", "reset"])
|
|
370
|
+
decisions_parser.add_argument("item_id", nargs="?")
|
|
371
|
+
decisions_parser.add_argument("--category", choices=["tools", "integrations", "skills", "llms"])
|
|
372
|
+
|
|
373
|
+
ollama_parser = subparsers.add_parser("ollama", help="inspect and manage local Ollama models")
|
|
374
|
+
ollama_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
375
|
+
ollama_parser.add_argument("action", nargs="?", default="status", choices=["status", "models", "pull", "update"])
|
|
376
|
+
ollama_parser.add_argument("model", nargs="?")
|
|
377
|
+
ollama_parser.add_argument("--yes", action="store_true", help="confirm Ollama model or update operation")
|
|
378
|
+
ollama_parser.add_argument("--dry-run", action="store_true", help="show Ollama operation without executing it")
|
|
379
|
+
|
|
309
380
|
llm_parser = subparsers.add_parser("llm", help="manage LLM backends")
|
|
310
381
|
llm_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
|
|
311
382
|
llm_parser.add_argument(
|
|
312
383
|
"action",
|
|
313
384
|
nargs="?",
|
|
314
385
|
default="list",
|
|
315
|
-
choices=["list", "doctor", "configure", "set-default", "preference"],
|
|
386
|
+
choices=["list", "doctor", "configure", "set-default", "default", "enable", "disable", "preference"],
|
|
316
387
|
)
|
|
317
388
|
llm_parser.add_argument("backend", nargs="?")
|
|
318
389
|
llm_parser.add_argument("preference_value", nargs="?")
|
|
@@ -401,6 +472,8 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
|
|
|
401
472
|
return dispatch_credential(args)
|
|
402
473
|
if command == "source":
|
|
403
474
|
return dispatch_source(args)
|
|
475
|
+
if command == "wizard":
|
|
476
|
+
return dispatch_wizard(args)
|
|
404
477
|
if command == "memory":
|
|
405
478
|
return dispatch_memory(args)
|
|
406
479
|
if command == "personality":
|
|
@@ -425,6 +498,14 @@ def dispatch(args: argparse.Namespace) -> dict[str, Any] | None:
|
|
|
425
498
|
return dispatch_permissions(args)
|
|
426
499
|
if command == "audit":
|
|
427
500
|
return dispatch_audit(args)
|
|
501
|
+
if command == "config":
|
|
502
|
+
return dispatch_config(args)
|
|
503
|
+
if command in {"tools", "integrations", "skills"}:
|
|
504
|
+
return dispatch_control_category(command, args)
|
|
505
|
+
if command == "decisions":
|
|
506
|
+
return dispatch_decisions(args)
|
|
507
|
+
if command == "ollama":
|
|
508
|
+
return dispatch_ollama(args)
|
|
428
509
|
if command == "llm":
|
|
429
510
|
return dispatch_llm(args)
|
|
430
511
|
if command == "install":
|
|
@@ -529,10 +610,15 @@ def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
529
610
|
command=args.host_command,
|
|
530
611
|
set_default=args.set_default,
|
|
531
612
|
)
|
|
532
|
-
if args.action
|
|
613
|
+
if args.action in {"set-default", "default"}:
|
|
533
614
|
if not args.backend:
|
|
534
|
-
raise DevKitError("llm
|
|
615
|
+
raise DevKitError(f"llm {args.action} requires a backend")
|
|
535
616
|
return set_default_backend(args.backend)
|
|
617
|
+
if args.action in {"enable", "disable"}:
|
|
618
|
+
if not args.backend:
|
|
619
|
+
raise DevKitError(f"llm {args.action} requires a backend")
|
|
620
|
+
state = "enabled" if args.action == "enable" else "disabled_by_user"
|
|
621
|
+
return set_decision("llms", args.backend, state, reason=f"llm {args.action} command")
|
|
536
622
|
if args.action == "preference":
|
|
537
623
|
if args.backend in {None, "show"}:
|
|
538
624
|
return llm_preference()
|
|
@@ -551,6 +637,88 @@ def dispatch_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
551
637
|
raise DevKitError(f"unsupported llm action: {args.action}")
|
|
552
638
|
|
|
553
639
|
|
|
640
|
+
def dispatch_config(args: argparse.Namespace) -> dict[str, Any]:
|
|
641
|
+
from cli.aikit.llm import config_path
|
|
642
|
+
|
|
643
|
+
if args.action == "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))
|
|
647
|
+
if args.action == "show":
|
|
648
|
+
return {
|
|
649
|
+
"kind": "config",
|
|
650
|
+
"status": "ok",
|
|
651
|
+
"path": str(config_path()),
|
|
652
|
+
"home": app_home_status(),
|
|
653
|
+
"decisions": list_decisions(),
|
|
654
|
+
"llm": llm_preference(),
|
|
655
|
+
"ollama": ollama_status(),
|
|
656
|
+
}
|
|
657
|
+
raise DevKitError(f"unsupported config action: {args.action}")
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def dispatch_control_category(command: str, args: argparse.Namespace) -> dict[str, Any]:
|
|
661
|
+
try:
|
|
662
|
+
category = command
|
|
663
|
+
if args.action == "list":
|
|
664
|
+
if args.item_id:
|
|
665
|
+
raise DevKitError(f"{command} list does not accept an item id")
|
|
666
|
+
payload = list_decisions(category)
|
|
667
|
+
payload["kind"] = command
|
|
668
|
+
return payload
|
|
669
|
+
if args.action in {"enable", "disable"}:
|
|
670
|
+
if not args.item_id:
|
|
671
|
+
raise DevKitError(f"{command} {args.action} requires an item id")
|
|
672
|
+
state = "enabled" if args.action == "enable" else "disabled_by_user"
|
|
673
|
+
payload = set_decision(category, args.item_id, state, reason=f"{command} {args.action} command")
|
|
674
|
+
payload["kind"] = command[:-1] if command.endswith("s") else command
|
|
675
|
+
return payload
|
|
676
|
+
except ValueError as exc:
|
|
677
|
+
raise DevKitError(str(exc)) from exc
|
|
678
|
+
raise DevKitError(f"unsupported {command} action: {args.action}")
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
def dispatch_decisions(args: argparse.Namespace) -> dict[str, Any]:
|
|
682
|
+
try:
|
|
683
|
+
if args.action == "list":
|
|
684
|
+
if args.item_id:
|
|
685
|
+
raise DevKitError("decisions list does not accept an item id")
|
|
686
|
+
return list_decisions(args.category)
|
|
687
|
+
if args.action == "reset":
|
|
688
|
+
if args.item_id:
|
|
689
|
+
raise DevKitError("decisions reset does not accept an item id")
|
|
690
|
+
return reset_decisions(args.category)
|
|
691
|
+
if args.action == "forget":
|
|
692
|
+
if not args.item_id:
|
|
693
|
+
raise DevKitError("decisions forget requires an item id")
|
|
694
|
+
category = args.category or "tools"
|
|
695
|
+
return forget_decision(category, args.item_id)
|
|
696
|
+
except ValueError as exc:
|
|
697
|
+
raise DevKitError(str(exc)) from exc
|
|
698
|
+
raise DevKitError(f"unsupported decisions action: {args.action}")
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def dispatch_ollama(args: argparse.Namespace) -> dict[str, Any]:
|
|
702
|
+
try:
|
|
703
|
+
if args.action == "status":
|
|
704
|
+
if args.model:
|
|
705
|
+
raise DevKitError("ollama status does not accept a model")
|
|
706
|
+
return ollama_status()
|
|
707
|
+
if args.action == "models":
|
|
708
|
+
if args.model:
|
|
709
|
+
raise DevKitError("ollama models does not accept a model")
|
|
710
|
+
return ollama_models()
|
|
711
|
+
if args.action == "pull":
|
|
712
|
+
return ollama_pull(args.model, yes=args.yes, dry_run=effective_dry_run(args))
|
|
713
|
+
if args.action == "update":
|
|
714
|
+
if args.model:
|
|
715
|
+
raise DevKitError("ollama update does not accept a model")
|
|
716
|
+
return ollama_update(yes=args.yes, dry_run=effective_dry_run(args))
|
|
717
|
+
except ValueError as exc:
|
|
718
|
+
raise DevKitError(str(exc)) from exc
|
|
719
|
+
raise DevKitError(f"unsupported ollama action: {args.action}")
|
|
720
|
+
|
|
721
|
+
|
|
554
722
|
def dispatch_install(args: argparse.Namespace) -> dict[str, Any]:
|
|
555
723
|
try:
|
|
556
724
|
return install_runtime(
|
|
@@ -645,6 +813,117 @@ def dispatch_source(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
645
813
|
raise DevKitError(f"unsupported source action: {args.action}")
|
|
646
814
|
|
|
647
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
|
+
|
|
648
927
|
def dispatch_memory(args: argparse.Namespace) -> dict[str, Any]:
|
|
649
928
|
if args.action == "show":
|
|
650
929
|
return show_memory(ROOT, agent_id=args.agent_id, source_id=args.source_id)
|
|
@@ -962,25 +1241,131 @@ def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
|
962
1241
|
return finalize_agent_session(natural_result, session, prompt, backend=args.llm)
|
|
963
1242
|
route = route_prompt(prompt)
|
|
964
1243
|
if route:
|
|
965
|
-
result =
|
|
1244
|
+
result = invoke_agentic_route(prompt, route)
|
|
966
1245
|
return finalize_agent_session(result, session, prompt, backend=args.llm)
|
|
967
1246
|
contextual_prompt = build_contextual_prompt(str(session["id"]), prompt)
|
|
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)
|
|
968
1255
|
result = invoke_agent_prompt(
|
|
969
|
-
|
|
1256
|
+
coordinator_prompt,
|
|
970
1257
|
args.llm,
|
|
971
1258
|
public_name=name,
|
|
972
1259
|
allow_fallback=not args.no_llm_fallback,
|
|
973
1260
|
)
|
|
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
|
+
}
|
|
1268
|
+
if result.get("ok"):
|
|
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)
|
|
1280
|
+
result["model_plan"] = model_plan
|
|
1281
|
+
result["local_llm_execution"] = local_llm_execution
|
|
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", [])
|
|
974
1287
|
result["prompt_length"] = len(prompt)
|
|
975
1288
|
result["session_context_applied"] = contextual_prompt != prompt
|
|
1289
|
+
result["local_context_applied"] = coordinator_prompt != contextual_prompt
|
|
976
1290
|
if result.get("response"):
|
|
977
1291
|
result["response"] = enforce_identity_response(str(result["response"]), prompt, name=name)
|
|
978
1292
|
result["identity"] = {"name": name, "source": "local"}
|
|
979
1293
|
return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or args.llm)
|
|
980
1294
|
|
|
981
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
|
+
|
|
982
1362
|
def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
|
|
983
1363
|
normalized = " ".join(prompt.lower().split())
|
|
1364
|
+
control_result = dispatch_natural_control_prompt(normalized)
|
|
1365
|
+
if control_result:
|
|
1366
|
+
control_result["prompt_received"] = True
|
|
1367
|
+
control_result["prompt_length"] = len(prompt)
|
|
1368
|
+
return control_result
|
|
984
1369
|
if "agenda" in normalized:
|
|
985
1370
|
if "amanha" in normalized or "amanhã" in normalized:
|
|
986
1371
|
payload = calendar_tomorrow()
|
|
@@ -1028,9 +1413,21 @@ def dispatch_natural_operational_prompt(prompt: str) -> dict[str, Any] | None:
|
|
|
1028
1413
|
return None
|
|
1029
1414
|
|
|
1030
1415
|
|
|
1416
|
+
def dispatch_natural_control_prompt(normalized_prompt: str) -> dict[str, Any] | None:
|
|
1417
|
+
return route_natural_control_prompt(ROOT, normalized_prompt)
|
|
1418
|
+
|
|
1419
|
+
|
|
1031
1420
|
def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str, Any]:
|
|
1032
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
|
|
1033
1427
|
route = route_prompt(prompt)
|
|
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)
|
|
1034
1431
|
plan: dict[str, Any] = {
|
|
1035
1432
|
"kind": "agent",
|
|
1036
1433
|
"status": "planned",
|
|
@@ -1047,6 +1444,10 @@ def build_agent_dry_run_plan(prompt: str, args: argparse.Namespace) -> dict[str,
|
|
|
1047
1444
|
"providers": {"used": [], "missing": [], "skipped": []},
|
|
1048
1445
|
"commands": [],
|
|
1049
1446
|
"permissions": [],
|
|
1447
|
+
"model_plan": model_plan,
|
|
1448
|
+
"review_gate": review_gate,
|
|
1449
|
+
"execution_plan": execution_plan,
|
|
1450
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1050
1451
|
"response": "Dry-run: nenhuma chamada LLM ou escrita externa foi executada.",
|
|
1051
1452
|
}
|
|
1052
1453
|
if "agenda" in normalized:
|
|
@@ -1120,7 +1521,8 @@ def finalize_agent_session(
|
|
|
1120
1521
|
return result
|
|
1121
1522
|
|
|
1122
1523
|
|
|
1123
|
-
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)
|
|
1124
1526
|
try:
|
|
1125
1527
|
source = resolve_source(
|
|
1126
1528
|
provider=route.get("provider"),
|
|
@@ -1131,43 +1533,96 @@ def invoke_deterministic_route(prompt: str, route: dict[str, Any]) -> dict[str,
|
|
|
1131
1533
|
raise DevKitError(str(exc)) from exc
|
|
1132
1534
|
|
|
1133
1535
|
if not source:
|
|
1134
|
-
|
|
1536
|
+
wizard = missing_source_wizard(prompt, route, root=ROOT)
|
|
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 = {
|
|
1135
1545
|
"kind": "agent",
|
|
1136
1546
|
"status": "needs-input",
|
|
1137
1547
|
"ok": False,
|
|
1138
1548
|
"requires_source": True,
|
|
1549
|
+
"provider": route.get("provider"),
|
|
1139
1550
|
"source_provider": route.get("provider"),
|
|
1140
1551
|
"prompt_received": True,
|
|
1141
1552
|
"prompt_length": len(prompt),
|
|
1142
1553
|
"route": route,
|
|
1143
1554
|
"napkin": napkin_context(ROOT, agent_id=route.get("agent_id")),
|
|
1144
|
-
"
|
|
1555
|
+
"execution_plan": execution_plan,
|
|
1556
|
+
"orchestration_trace": execution_plan.get("trace", []),
|
|
1557
|
+
"setup_wizard": wizard,
|
|
1558
|
+
"next_question": wizard.get("next_question"),
|
|
1559
|
+
"message": wizard.get("message"),
|
|
1145
1560
|
"next_steps": [
|
|
1146
|
-
"
|
|
1147
|
-
"
|
|
1148
|
-
"
|
|
1561
|
+
"Responda a pergunta do wizard para autorizar ou negar a configuracao desta fonte.",
|
|
1562
|
+
"Se preferir teste local, configure uma source com fixture sem armazenar segredos.",
|
|
1563
|
+
"O prompt original sera retomado apos a fonte reutilizavel ser configurada.",
|
|
1149
1564
|
],
|
|
1150
1565
|
"exit_code": 2,
|
|
1151
1566
|
}
|
|
1567
|
+
return persist_setup_wizard_payload(payload, execution_plan=execution_plan, route=route)
|
|
1152
1568
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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 {}
|
|
1156
1577
|
response = result.get("stdout") or result.get("error") or ""
|
|
1157
1578
|
record_usage(prompt, route=route, source_id=str(source["id"]))
|
|
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
|
+
}
|
|
1594
|
+
if result.get("ok"):
|
|
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)
|
|
1158
1606
|
return {
|
|
1159
1607
|
"kind": "agent",
|
|
1160
|
-
"status":
|
|
1161
|
-
"ok":
|
|
1162
|
-
"mode": "
|
|
1608
|
+
"status": review_payload.get("status"),
|
|
1609
|
+
"ok": bool(review_payload.get("ok")),
|
|
1610
|
+
"mode": "agentic-route",
|
|
1611
|
+
"legacy_mode": "deterministic-route",
|
|
1163
1612
|
"prompt_received": True,
|
|
1164
1613
|
"prompt_length": len(prompt),
|
|
1165
1614
|
"route": route,
|
|
1166
1615
|
"source": public_source(source),
|
|
1167
1616
|
"napkin": napkin_context(ROOT, agent_id=route.get("agent_id"), source_id=str(source["id"])),
|
|
1168
|
-
"
|
|
1617
|
+
"model_plan": model_plan,
|
|
1618
|
+
"review_gate": review_gate,
|
|
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,
|
|
1169
1624
|
"result": result,
|
|
1170
|
-
"exit_code":
|
|
1625
|
+
"exit_code": review_payload.get("exit_code", result.get("exit_code", 0 if executed and not blocked else 1)),
|
|
1171
1626
|
}
|
|
1172
1627
|
|
|
1173
1628
|
|
|
@@ -1361,9 +1816,9 @@ def run_capability(
|
|
|
1361
1816
|
next_steps=guardrail["next_steps"],
|
|
1362
1817
|
exit_code=2,
|
|
1363
1818
|
)
|
|
1364
|
-
readiness = evaluate_provider_requirements(ROOT, data)
|
|
1819
|
+
readiness = evaluate_provider_requirements(ROOT, data, capability_args)
|
|
1365
1820
|
if not readiness["ready"]:
|
|
1366
|
-
|
|
1821
|
+
payload = run_payload(
|
|
1367
1822
|
status=readiness["status"],
|
|
1368
1823
|
agent=summarize_agent(agent),
|
|
1369
1824
|
capability=data.get("id", capability_id),
|
|
@@ -1377,6 +1832,18 @@ def run_capability(
|
|
|
1377
1832
|
artifacts=readiness["artifacts"],
|
|
1378
1833
|
exit_code=readiness.get("exit_code"),
|
|
1379
1834
|
)
|
|
1835
|
+
wizard = setup_wizard_from_readiness(readiness, agent=summarize_agent(agent), capability_id=str(data.get("id", capability_id)))
|
|
1836
|
+
if wizard:
|
|
1837
|
+
payload["setup_wizard"] = wizard
|
|
1838
|
+
payload["next_question"] = wizard.get("next_question")
|
|
1839
|
+
payload["configuration_agent"] = wizard.get("owner_agent")
|
|
1840
|
+
payload["next_steps"] = [
|
|
1841
|
+
"Responda a pergunta do wizard para autorizar ou negar a configuracao deste provider.",
|
|
1842
|
+
"Informe uma referencia segura de credencial por variavel de ambiente, arquivo ou cadeia nativa quando solicitado.",
|
|
1843
|
+
"Reexecute ou retome a mesma capability depois que a configuracao estiver salva.",
|
|
1844
|
+
]
|
|
1845
|
+
payload = persist_setup_wizard_payload(payload)
|
|
1846
|
+
return payload
|
|
1380
1847
|
|
|
1381
1848
|
runner_ref = (data.get("entrypoint", {}) or {}).get("runner")
|
|
1382
1849
|
if not runner_ref:
|
|
@@ -1495,6 +1962,26 @@ def supports_runtime_source(agent_id: str, capability_id: str) -> bool:
|
|
|
1495
1962
|
return agent_id == "azure-devops-orchestrator" and capability_id == "read-card"
|
|
1496
1963
|
|
|
1497
1964
|
|
|
1965
|
+
def setup_wizard_from_readiness(readiness: dict[str, Any], *, agent: dict[str, Any], capability_id: str) -> dict[str, Any] | None:
|
|
1966
|
+
providers = readiness.get("providers") if isinstance(readiness.get("providers"), dict) else {}
|
|
1967
|
+
missing = providers.get("missing") or []
|
|
1968
|
+
if not missing:
|
|
1969
|
+
return None
|
|
1970
|
+
provider_id = str(missing[0])
|
|
1971
|
+
details = providers.get("details") or []
|
|
1972
|
+
detail = next((item for item in details if isinstance(item, dict) and item.get("id") == provider_id), {})
|
|
1973
|
+
try:
|
|
1974
|
+
return provider_wizard_from_requirement(
|
|
1975
|
+
ROOT,
|
|
1976
|
+
provider_id,
|
|
1977
|
+
agent_id=str(agent.get("id") or ""),
|
|
1978
|
+
capability_id=capability_id,
|
|
1979
|
+
reason=str(detail.get("purpose") or "Provider is required but not configured."),
|
|
1980
|
+
)
|
|
1981
|
+
except Exception:
|
|
1982
|
+
return None
|
|
1983
|
+
|
|
1984
|
+
|
|
1498
1985
|
def doctor(project: str | None = None, home: str | None = None, scope: str = "auto") -> dict[str, Any]:
|
|
1499
1986
|
agents = list_agents()
|
|
1500
1987
|
capabilities = list_all_capabilities()
|
|
@@ -1704,6 +2191,8 @@ def print_human(result: dict[str, Any]) -> None:
|
|
|
1704
2191
|
print_source_configure(result)
|
|
1705
2192
|
elif kind == "source-remove":
|
|
1706
2193
|
print_source_remove(result)
|
|
2194
|
+
elif kind in {"wizards", "wizard"}:
|
|
2195
|
+
print_wizard(result)
|
|
1707
2196
|
elif kind == "memory":
|
|
1708
2197
|
print_memory(result)
|
|
1709
2198
|
elif kind == "memory-path":
|
|
@@ -1748,6 +2237,12 @@ def print_human(result: dict[str, Any]) -> None:
|
|
|
1748
2237
|
print_permissions(result)
|
|
1749
2238
|
elif kind in {"audit", "audit-entry", "audit-export"}:
|
|
1750
2239
|
print_audit(result)
|
|
2240
|
+
elif kind == "config":
|
|
2241
|
+
print_config(result)
|
|
2242
|
+
elif kind in {"tools", "tool", "integrations", "integration", "skills", "skill", "decisions", "decision", "decisions-reset"}:
|
|
2243
|
+
print_control(result)
|
|
2244
|
+
elif kind in {"ollama-status", "ollama-models", "ollama-pull", "ollama-update"}:
|
|
2245
|
+
print_ollama(result)
|
|
1751
2246
|
elif kind == "install":
|
|
1752
2247
|
print_install(result)
|
|
1753
2248
|
else:
|
|
@@ -1875,6 +2370,15 @@ def print_agent_response(result: dict[str, Any]) -> None:
|
|
|
1875
2370
|
print(result.get("response", ""))
|
|
1876
2371
|
return
|
|
1877
2372
|
print(result.get("message") or result.get("response") or "Agent execution did not complete.")
|
|
2373
|
+
question = result.get("next_question") or ((result.get("setup_wizard") or {}).get("next_question") if isinstance(result.get("setup_wizard"), dict) else None)
|
|
2374
|
+
if isinstance(question, dict) and question.get("text"):
|
|
2375
|
+
print(f"\nPergunta: {question['text']}")
|
|
2376
|
+
if question.get("type") == "confirm":
|
|
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>")
|
|
1878
2382
|
if result.get("llm_backend"):
|
|
1879
2383
|
print(f"Requested backend: {result['llm_backend']}")
|
|
1880
2384
|
if result.get("next_steps"):
|
|
@@ -1883,6 +2387,33 @@ def print_agent_response(result: dict[str, Any]) -> None:
|
|
|
1883
2387
|
print(f"- {step}")
|
|
1884
2388
|
|
|
1885
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
|
+
|
|
1886
2417
|
def print_sources(result: dict[str, Any]) -> None:
|
|
1887
2418
|
print(f"Sources config: {result['config_path']}")
|
|
1888
2419
|
if not result["items"]:
|
|
@@ -2268,6 +2799,58 @@ def print_credential_backends(result: dict[str, Any]) -> None:
|
|
|
2268
2799
|
print(f"- {item}")
|
|
2269
2800
|
|
|
2270
2801
|
|
|
2802
|
+
def print_config(result: dict[str, Any]) -> None:
|
|
2803
|
+
print(f"Config: {result.get('path')}")
|
|
2804
|
+
if result.get("llm"):
|
|
2805
|
+
print(f"Primary LLM: {(result['llm'] or {}).get('primary') or '-'}")
|
|
2806
|
+
if result.get("ollama"):
|
|
2807
|
+
print(f"Ollama: {(result['ollama'] or {}).get('status')}")
|
|
2808
|
+
|
|
2809
|
+
|
|
2810
|
+
def print_control(result: dict[str, Any]) -> None:
|
|
2811
|
+
kind = result.get("kind")
|
|
2812
|
+
if kind == "decisions-reset":
|
|
2813
|
+
print(f"Decisions reset: {result.get('category') or 'all'}")
|
|
2814
|
+
print(f"Path: {result.get('path')}")
|
|
2815
|
+
return
|
|
2816
|
+
if "items" in result:
|
|
2817
|
+
print(f"{kind}:")
|
|
2818
|
+
for item in result.get("items") or []:
|
|
2819
|
+
print(f"- {item.get('category')}:{item.get('id')} {item.get('state')}")
|
|
2820
|
+
if not result.get("items"):
|
|
2821
|
+
print("- none")
|
|
2822
|
+
return
|
|
2823
|
+
item = result.get("item") or result
|
|
2824
|
+
print(f"{item.get('category') or result.get('category')}:{item.get('id') or result.get('id')} {item.get('state') or result.get('state')}")
|
|
2825
|
+
|
|
2826
|
+
|
|
2827
|
+
def print_ollama(result: dict[str, Any]) -> None:
|
|
2828
|
+
kind = result.get("kind")
|
|
2829
|
+
if kind == "ollama-status":
|
|
2830
|
+
print(f"Ollama: {result.get('status')}")
|
|
2831
|
+
print(f"Binary: {result.get('binary') or '-'}")
|
|
2832
|
+
print(f"Version: {result.get('version') or '-'}")
|
|
2833
|
+
daemon = result.get("daemon") or {}
|
|
2834
|
+
print(f"Daemon: {daemon.get('status') or '-'}")
|
|
2835
|
+
print(f"Models: {result.get('model_count', 0)}")
|
|
2836
|
+
if result.get("install_plan"):
|
|
2837
|
+
print(f"Install: {(result['install_plan'] or {}).get('command')}")
|
|
2838
|
+
return
|
|
2839
|
+
if kind == "ollama-models":
|
|
2840
|
+
print(f"Ollama models: {result.get('status')}")
|
|
2841
|
+
for item in result.get("items") or []:
|
|
2842
|
+
print(f"- {item.get('name')} {item.get('size') or '-'}")
|
|
2843
|
+
if not result.get("items"):
|
|
2844
|
+
print("- none")
|
|
2845
|
+
return
|
|
2846
|
+
print(f"{kind}: {result.get('status')}")
|
|
2847
|
+
if result.get("command"):
|
|
2848
|
+
command = result["command"]
|
|
2849
|
+
print("Command: " + (" ".join(command) if isinstance(command, list) else str(command)))
|
|
2850
|
+
if result.get("message"):
|
|
2851
|
+
print(result["message"])
|
|
2852
|
+
|
|
2853
|
+
|
|
2271
2854
|
def print_install(result: dict[str, Any]) -> None:
|
|
2272
2855
|
print(f"AI DevKit install: {result['status']}")
|
|
2273
2856
|
print(f"Scope: {result['scope']}")
|