agent-devkit 0.2.0 → 0.3.0
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 +48 -6
- package/bin/agent.mjs +133 -7
- package/package.json +1 -1
- package/runtime/README.md +187 -5
- package/runtime/agent +31 -5
- package/runtime/agents/README.md +18 -0
- package/runtime/agents/contribution-reviewer/AGENTS.md +8 -0
- package/runtime/agents/contribution-reviewer/README.md +8 -0
- package/runtime/agents/contribution-reviewer/agent.yaml +40 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/capability.yaml +27 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/workflow.md +6 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/capability.yaml +25 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/workflow.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/capability.yaml +26 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/workflow.md +6 -0
- package/runtime/agents/contribution-reviewer/infra/README.md +6 -0
- package/runtime/agents/contribution-reviewer/knowledge/context.md +8 -0
- package/runtime/agents/contribution-reviewer/knowledge/system.md +8 -0
- package/runtime/agents/contribution-reviewer/templates/README.md +3 -0
- package/runtime/agents/knowledge-author/AGENTS.md +7 -0
- package/runtime/agents/knowledge-author/README.md +7 -0
- package/runtime/agents/knowledge-author/agent.yaml +37 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/capability.yaml +30 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-author/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-author/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-author/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-author/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-curator/AGENTS.md +7 -0
- package/runtime/agents/knowledge-curator/README.md +6 -0
- package/runtime/agents/knowledge-curator/agent.yaml +37 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/capability.yaml +29 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/decision-rules.md +6 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/workflow.md +7 -0
- package/runtime/agents/knowledge-curator/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-curator/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-curator/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-curator/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-infra-builder/AGENTS.md +8 -0
- package/runtime/agents/knowledge-infra-builder/README.md +8 -0
- package/runtime/agents/knowledge-infra-builder/agent.yaml +38 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/capability.yaml +30 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/decision-rules.md +6 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/workflow.md +7 -0
- package/runtime/agents/knowledge-infra-builder/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-infra-builder/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-infra-builder/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-infra-builder/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-owner/AGENTS.md +7 -0
- package/runtime/agents/knowledge-owner/README.md +6 -0
- package/runtime/agents/knowledge-owner/agent.yaml +37 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/capability.yaml +28 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-owner/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-owner/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-owner/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-owner/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-reviewer/AGENTS.md +7 -0
- package/runtime/agents/knowledge-reviewer/README.md +7 -0
- package/runtime/agents/knowledge-reviewer/agent.yaml +36 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/capability.yaml +26 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-reviewer/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-reviewer/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-reviewer/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-reviewer/templates/.gitkeep +1 -0
- package/runtime/agents/local-memory-manager/AGENTS.md +5 -0
- package/runtime/agents/local-memory-manager/README.md +7 -0
- package/runtime/agents/local-memory-manager/agent.yaml +38 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/capability.yaml +19 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/decision-rules.md +5 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/workflow.md +6 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/capability.yaml +19 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/decision-rules.md +5 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/workflow.md +5 -0
- package/runtime/agents/local-memory-manager/infra/.gitkeep +1 -0
- package/runtime/agents/local-memory-manager/knowledge/context.md +4 -0
- package/runtime/agents/local-memory-manager/knowledge/system.md +4 -0
- package/runtime/agents/local-memory-manager/templates/.gitkeep +1 -0
- package/runtime/agents/memory-sync-manager/AGENTS.md +7 -0
- package/runtime/agents/memory-sync-manager/README.md +7 -0
- package/runtime/agents/memory-sync-manager/agent.yaml +37 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/capability.yaml +29 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/decision-rules.md +6 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/workflow.md +7 -0
- package/runtime/agents/memory-sync-manager/infra/.gitkeep +1 -0
- package/runtime/agents/memory-sync-manager/knowledge/context.md +4 -0
- package/runtime/agents/memory-sync-manager/knowledge/system.md +4 -0
- package/runtime/agents/memory-sync-manager/templates/.gitkeep +1 -0
- package/runtime/agents/shared-memory-curator/AGENTS.md +5 -0
- package/runtime/agents/shared-memory-curator/README.md +6 -0
- package/runtime/agents/shared-memory-curator/agent.yaml +38 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/infra/.gitkeep +1 -0
- package/runtime/agents/shared-memory-curator/knowledge/context.md +5 -0
- package/runtime/agents/shared-memory-curator/knowledge/system.md +4 -0
- package/runtime/agents/shared-memory-curator/templates/.gitkeep +1 -0
- package/runtime/cli/README.md +35 -4
- package/runtime/cli/aikit/__init__.py +1 -1
- package/runtime/cli/aikit/agent_registry.py +4 -2
- package/runtime/cli/aikit/agentic_commands.py +158 -0
- package/runtime/cli/aikit/app_home.py +1 -0
- package/runtime/cli/aikit/audit.py +16 -6
- package/runtime/cli/aikit/catalog.py +278 -8
- package/runtime/cli/aikit/cli_dispatch.py +489 -13
- package/runtime/cli/aikit/cli_parser.py +145 -7
- package/runtime/cli/aikit/contribution.py +132 -2
- package/runtime/cli/aikit/doctor_runtime.py +85 -0
- package/runtime/cli/aikit/eval.py +356 -10
- package/runtime/cli/aikit/human_output.py +310 -4
- package/runtime/cli/aikit/interactive_wizard.py +148 -0
- package/runtime/cli/aikit/knowledge_base.py +1067 -0
- package/runtime/cli/aikit/llm.py +12 -4
- package/runtime/cli/aikit/local_artifacts.py +444 -0
- package/runtime/cli/aikit/local_llm.py +161 -0
- package/runtime/cli/aikit/main.py +15 -0
- package/runtime/cli/aikit/mcp_manifest.py +798 -0
- package/runtime/cli/aikit/mcp_tools.py +643 -5
- package/runtime/cli/aikit/memory.py +405 -0
- package/runtime/cli/aikit/mini_brain.py +20 -1
- package/runtime/cli/aikit/natural_prompt_runtime.py +125 -1
- package/runtime/cli/aikit/ollama.py +64 -15
- package/runtime/cli/aikit/onboarding.py +551 -0
- package/runtime/cli/aikit/output.py +67 -0
- package/runtime/cli/aikit/prompt_injection.py +12 -1
- package/runtime/cli/aikit/roadmap_cli.py +1 -1
- package/runtime/cli/aikit/secrets.py +3 -2
- package/runtime/cli/aikit/setup_wizard_payload.py +3 -0
- package/runtime/cli/aikit/shared_memory.py +415 -0
- package/runtime/cli/aikit/specialist_readiness.py +152 -0
- package/runtime/cli/aikit/tasks.py +104 -1
- package/runtime/cli/aikit/team.py +380 -0
- package/runtime/cli/aikit/toolchain.py +7 -2
- package/runtime/cli/aikit/workflows.py +115 -14
- package/runtime/providers/knowledge-github.yaml +40 -0
- package/runtime/providers/knowledge-google-drive.yaml +32 -0
- package/runtime/providers/knowledge-local.yaml +26 -0
- package/runtime/providers/knowledge-notion.yaml +32 -0
- package/runtime/providers/knowledge-obsidian.yaml +24 -0
- package/runtime/providers/knowledge-onedrive.yaml +36 -0
- package/runtime/providers/knowledge-s3.yaml +45 -0
- package/runtime/providers/knowledge-sharepoint.yaml +39 -0
- package/runtime/providers/knowledge-supabase.yaml +43 -0
- package/runtime/providers/knowledge-vector.yaml +39 -0
- package/runtime/requirements.txt +6 -0
- package/runtime/scripts/docker-cli-qa.sh +453 -0
- package/runtime/scripts/release-catalog-snapshot.json +55 -4
- package/runtime/scripts/release-gate.py +54 -13
- package/runtime/tooling/toolchain.yaml +92 -0
- package/runtime/vendor/skills/napkin/napkin.md +21 -7
- package/runtime/workflows/azure-card-analysis/README.md +3 -0
- package/runtime/workflows/azure-card-analysis/workflow.yaml +30 -0
- package/runtime/workflows/daily-pr-review/README.md +3 -0
- package/runtime/workflows/daily-pr-review/workflow.yaml +31 -0
- package/runtime/workflows/incident-analysis/README.md +3 -0
- package/runtime/workflows/incident-analysis/workflow.yaml +33 -0
- package/runtime/workflows/release-prep/README.md +3 -0
- package/runtime/workflows/release-prep/workflow.yaml +30 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import os
|
|
6
7
|
import platform
|
|
7
8
|
import shutil
|
|
8
9
|
import subprocess
|
|
@@ -10,17 +11,31 @@ import urllib.error
|
|
|
10
11
|
import urllib.request
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
14
|
+
from cli.aikit.app_home import config_path
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
OLLAMA_TIMEOUT_SECONDS = 120
|
|
15
18
|
DEFAULT_BASE_URL = "http://localhost:11434"
|
|
19
|
+
DEFAULT_RECOMMENDED_MODELS = [
|
|
20
|
+
{"name": "qwen3:0.6b", "family": "mini-brain", "recommended_for": "setup help, short summaries and lightweight intent classification"},
|
|
21
|
+
{"name": "qwen2.5-coder", "family": "coding", "recommended_for": "operational code reading and generation"},
|
|
22
|
+
{"name": "deepseek-coder", "family": "coding", "recommended_for": "code analysis and mechanical refactors"},
|
|
23
|
+
{"name": "deepseek-r1", "family": "reasoning", "recommended_for": "local reasoning drafts with mandatory review"},
|
|
24
|
+
{"name": "llama3.2", "family": "general", "recommended_for": "general local summaries and classification"},
|
|
25
|
+
{"name": "mistral", "family": "general", "recommended_for": "lightweight operational summaries"},
|
|
26
|
+
{"name": "gemma", "family": "classification", "recommended_for": "small extraction and classification tasks"},
|
|
27
|
+
]
|
|
16
28
|
|
|
17
29
|
|
|
18
30
|
def ollama_status(*, base_url: str = DEFAULT_BASE_URL) -> dict[str, Any]:
|
|
31
|
+
base_url = os.environ.get("OLLAMA_BASE_URL") or base_url
|
|
19
32
|
binary = shutil.which("ollama")
|
|
20
33
|
version = command_output(["ollama", "--version"]) if binary else None
|
|
21
34
|
daemon = daemon_status(base_url) if binary else {"status": "unknown", "message": "Ollama binary is not installed."}
|
|
22
35
|
models = list_local_models(binary_available=bool(binary))
|
|
23
|
-
status = "
|
|
36
|
+
status = "missing"
|
|
37
|
+
if binary:
|
|
38
|
+
status = "ok" if daemon.get("status") == "ok" else "partial"
|
|
24
39
|
return {
|
|
25
40
|
"kind": "ollama-status",
|
|
26
41
|
"status": status,
|
|
@@ -52,10 +67,12 @@ def ollama_pull(model: str | None, *, yes: bool = False, dry_run: bool = False)
|
|
|
52
67
|
binary = shutil.which("ollama")
|
|
53
68
|
command = ["ollama", "pull", model]
|
|
54
69
|
if dry_run or not yes:
|
|
70
|
+
needs_confirmation = not dry_run and not yes
|
|
55
71
|
return {
|
|
56
72
|
"kind": "ollama-pull",
|
|
57
73
|
"status": "planned" if dry_run else "needs-confirmation",
|
|
58
74
|
"ok": bool(dry_run),
|
|
75
|
+
"exit_code": 2 if needs_confirmation else 0,
|
|
59
76
|
"model": model,
|
|
60
77
|
"binary": binary,
|
|
61
78
|
"command": command,
|
|
@@ -91,10 +108,12 @@ def ollama_pull(model: str | None, *, yes: bool = False, dry_run: bool = False)
|
|
|
91
108
|
def ollama_update(*, yes: bool = False, dry_run: bool = False) -> dict[str, Any]:
|
|
92
109
|
command = update_command()
|
|
93
110
|
if dry_run or not yes:
|
|
111
|
+
needs_confirmation = not dry_run and not yes
|
|
94
112
|
return {
|
|
95
113
|
"kind": "ollama-update",
|
|
96
114
|
"status": "planned" if dry_run else "needs-confirmation",
|
|
97
115
|
"ok": bool(dry_run),
|
|
116
|
+
"exit_code": 2 if needs_confirmation else 0,
|
|
98
117
|
"command": command,
|
|
99
118
|
"dry_run": dry_run,
|
|
100
119
|
"yes": yes,
|
|
@@ -142,26 +161,56 @@ def parse_ollama_list(output: str) -> list[dict[str, Any]]:
|
|
|
142
161
|
|
|
143
162
|
|
|
144
163
|
def recommended_models(*, installed: set[str]) -> list[dict[str, Any]]:
|
|
145
|
-
catalog =
|
|
146
|
-
("qwen3:0.6b", "mini-brain", "setup help, short summaries and lightweight intent classification"),
|
|
147
|
-
("qwen2.5-coder", "coding", "operational code reading and generation"),
|
|
148
|
-
("deepseek-coder", "coding", "code analysis and mechanical refactors"),
|
|
149
|
-
("deepseek-r1", "reasoning", "local reasoning drafts with mandatory review"),
|
|
150
|
-
("llama3.2", "general", "general local summaries and classification"),
|
|
151
|
-
("mistral", "general", "lightweight operational summaries"),
|
|
152
|
-
("gemma", "classification", "small extraction and classification tasks"),
|
|
153
|
-
]
|
|
164
|
+
catalog = model_catalog()
|
|
154
165
|
return [
|
|
155
166
|
{
|
|
156
|
-
"name": name,
|
|
157
|
-
"family": family,
|
|
158
|
-
"recommended_for": recommended_for,
|
|
159
|
-
"installed": any(
|
|
167
|
+
"name": item["name"],
|
|
168
|
+
"family": item["family"],
|
|
169
|
+
"recommended_for": item["recommended_for"],
|
|
170
|
+
"installed": any(model == item["name"] or model.startswith(f"{item['name']}:") for model in installed),
|
|
171
|
+
"source": item["source"],
|
|
160
172
|
}
|
|
161
|
-
for
|
|
173
|
+
for item in catalog
|
|
162
174
|
]
|
|
163
175
|
|
|
164
176
|
|
|
177
|
+
def model_catalog() -> list[dict[str, str]]:
|
|
178
|
+
items = {item["name"]: {**item, "source": "default"} for item in DEFAULT_RECOMMENDED_MODELS}
|
|
179
|
+
for item in configured_model_catalog():
|
|
180
|
+
name = str(item.get("name") or "").strip()
|
|
181
|
+
if not name:
|
|
182
|
+
continue
|
|
183
|
+
items[name] = {
|
|
184
|
+
"name": name,
|
|
185
|
+
"family": str(item.get("family") or "custom").strip() or "custom",
|
|
186
|
+
"recommended_for": str(item.get("recommended_for") or item.get("purpose") or "configured local model").strip(),
|
|
187
|
+
"source": "config",
|
|
188
|
+
}
|
|
189
|
+
return list(items.values())
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def configured_model_catalog() -> list[dict[str, Any]]:
|
|
193
|
+
path = config_path()
|
|
194
|
+
if not path.exists():
|
|
195
|
+
return []
|
|
196
|
+
try:
|
|
197
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
198
|
+
except (OSError, json.JSONDecodeError):
|
|
199
|
+
return []
|
|
200
|
+
if not isinstance(payload, dict):
|
|
201
|
+
return []
|
|
202
|
+
local_llm = payload.get("local_llm") if isinstance(payload.get("local_llm"), dict) else {}
|
|
203
|
+
mini_brain = payload.get("mini_brain") if isinstance(payload.get("mini_brain"), dict) else {}
|
|
204
|
+
for value in (
|
|
205
|
+
local_llm.get("recommended_models"),
|
|
206
|
+
local_llm.get("models"),
|
|
207
|
+
mini_brain.get("recommended_models"),
|
|
208
|
+
):
|
|
209
|
+
if isinstance(value, list):
|
|
210
|
+
return [item for item in value if isinstance(item, dict)]
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
|
|
165
214
|
def daemon_status(base_url: str) -> dict[str, Any]:
|
|
166
215
|
url = base_url.rstrip("/") + "/api/tags"
|
|
167
216
|
try:
|
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
"""First-run and no-argument onboarding for Agent DevKit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from cli.aikit.agent_registry import load_agent_registry
|
|
9
|
+
from cli.aikit.app_home import app_home_status
|
|
10
|
+
from cli.aikit.llm import doctor_backends, list_backends
|
|
11
|
+
from cli.aikit.memory import ensure_memory
|
|
12
|
+
from cli.aikit.ollama import ollama_status
|
|
13
|
+
from cli.aikit.personality import load_personality
|
|
14
|
+
from cli.aikit.sessions import list_sessions, show_session
|
|
15
|
+
from cli.aikit.setup_wizard import setup_wizard
|
|
16
|
+
from cli.aikit.sources import list_sources, source_status
|
|
17
|
+
from cli.aikit.specialist_readiness import specialist_readiness
|
|
18
|
+
from cli.aikit.tasks import load_tasks, public_task, task_is_due, list_tasks
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
ONBOARDING_MODE_SCHEMA_VERSION = "agent-devkit.onboarding-plan/v1"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def onboarding_status(root: Path) -> dict[str, Any]:
|
|
25
|
+
"""Return a local, deterministic startup snapshot for `agent` without args."""
|
|
26
|
+
|
|
27
|
+
home = app_home_status()
|
|
28
|
+
memory = ensure_memory()
|
|
29
|
+
personality = load_personality()
|
|
30
|
+
sessions = summarize_sessions()
|
|
31
|
+
setup = setup_wizard(root, dry_run=True, yes=False)
|
|
32
|
+
llms = summarize_llms()
|
|
33
|
+
ollama = ollama_status()
|
|
34
|
+
sources = summarize_sources()
|
|
35
|
+
tasks = summarize_tasks()
|
|
36
|
+
specialists = specialist_readiness(root)
|
|
37
|
+
|
|
38
|
+
blockers = startup_blockers(setup=setup, llms=llms)
|
|
39
|
+
suggestions = suggested_actions(
|
|
40
|
+
memory=memory,
|
|
41
|
+
personality=personality,
|
|
42
|
+
sessions=sessions,
|
|
43
|
+
setup=setup,
|
|
44
|
+
llms=llms,
|
|
45
|
+
ollama=ollama,
|
|
46
|
+
sources=sources,
|
|
47
|
+
tasks=tasks,
|
|
48
|
+
specialists=specialists,
|
|
49
|
+
)
|
|
50
|
+
status = "ready"
|
|
51
|
+
if blockers:
|
|
52
|
+
status = "needs-setup"
|
|
53
|
+
elif suggestions:
|
|
54
|
+
status = "needs-attention"
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
"kind": "onboarding",
|
|
58
|
+
"schema_version": "agent-devkit.onboarding/v1",
|
|
59
|
+
"status": status,
|
|
60
|
+
"agent": {
|
|
61
|
+
"name": personality.get("agent_name") or "Agent DevKit",
|
|
62
|
+
"tone": personality.get("tone"),
|
|
63
|
+
"detail_level": personality.get("detail_level"),
|
|
64
|
+
"language": personality.get("language"),
|
|
65
|
+
"user_name": personality.get("user_name"),
|
|
66
|
+
},
|
|
67
|
+
"home": home,
|
|
68
|
+
"memory": {
|
|
69
|
+
"status": memory.get("status"),
|
|
70
|
+
"home": memory.get("home"),
|
|
71
|
+
"created": memory.get("created") or [],
|
|
72
|
+
"file_count": len(memory.get("files") or []),
|
|
73
|
+
},
|
|
74
|
+
"sessions": {
|
|
75
|
+
"status": sessions.get("status"),
|
|
76
|
+
"home": sessions.get("home"),
|
|
77
|
+
"active_session_id": sessions.get("active_session_id"),
|
|
78
|
+
"count": len(sessions.get("items") or []),
|
|
79
|
+
"active": sessions.get("active"),
|
|
80
|
+
"recent": sessions.get("recent") or [],
|
|
81
|
+
},
|
|
82
|
+
"llm": llms,
|
|
83
|
+
"ollama": summarize_ollama(ollama),
|
|
84
|
+
"toolchain": summarize_toolchain(setup.get("toolchain") or {}),
|
|
85
|
+
"sources": sources,
|
|
86
|
+
"tasks": tasks,
|
|
87
|
+
"specialists": summarize_specialists(specialists),
|
|
88
|
+
"blockers": blockers,
|
|
89
|
+
"suggested_actions": suggestions,
|
|
90
|
+
"onboarding_modes": onboarding_modes(),
|
|
91
|
+
"startup_flow": startup_flow(status=status, sessions=sessions, tasks=tasks, blockers=blockers),
|
|
92
|
+
"assistant_prompt": assistant_prompt(status=status, sessions=sessions, tasks=tasks, blockers=blockers, actions=suggestions),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def onboarding_plan(root: Path, mode: str) -> dict[str, Any]:
|
|
97
|
+
selected_mode = normalize_onboarding_mode(mode)
|
|
98
|
+
status = onboarding_status(root)
|
|
99
|
+
registry = load_agent_registry(root)
|
|
100
|
+
minimal_steps = [
|
|
101
|
+
plan_step(
|
|
102
|
+
"personality",
|
|
103
|
+
"Configurar nome publico, usuario, idioma, tom e nivel de detalhe.",
|
|
104
|
+
"agent setup personality",
|
|
105
|
+
write_policy="local_config_write",
|
|
106
|
+
),
|
|
107
|
+
plan_step(
|
|
108
|
+
"coordinator-llm",
|
|
109
|
+
"Registrar Claude Code, Codex CLI ou API como coordenador/planejador/revisor.",
|
|
110
|
+
"agent llm list",
|
|
111
|
+
write_policy="local_config_write",
|
|
112
|
+
),
|
|
113
|
+
plan_step(
|
|
114
|
+
"mini-brain",
|
|
115
|
+
"Habilitar Qwen3-0.6B via Ollama para conversa simples, setup e tarefas operacionais leves.",
|
|
116
|
+
"agent setup mini-brain --dry-run",
|
|
117
|
+
write_policy="local_config_write",
|
|
118
|
+
model="qwen3:0.6b",
|
|
119
|
+
),
|
|
120
|
+
plan_step(
|
|
121
|
+
"sessions-and-memory",
|
|
122
|
+
"Inicializar memoria local, sessoes, preferencias e identidade em .agent-devkit.",
|
|
123
|
+
"agent memory show",
|
|
124
|
+
write_policy="local_config_write",
|
|
125
|
+
),
|
|
126
|
+
]
|
|
127
|
+
complete_steps = minimal_steps + [
|
|
128
|
+
plan_step(
|
|
129
|
+
"toolchain",
|
|
130
|
+
"Revisar CLIs locais e planejar instalacao com opt-in por ferramenta.",
|
|
131
|
+
"agent toolchain doctor",
|
|
132
|
+
write_policy="confirm",
|
|
133
|
+
),
|
|
134
|
+
plan_step(
|
|
135
|
+
"providers-and-sources",
|
|
136
|
+
"Preparar configuracao sob demanda para providers, sources e credenciais por referencia segura.",
|
|
137
|
+
"agent source list",
|
|
138
|
+
write_policy="local_config_write",
|
|
139
|
+
),
|
|
140
|
+
plan_step(
|
|
141
|
+
"specialist-catalog",
|
|
142
|
+
"Validar catalogo completo e readiness de agentes especialistas por provider/source.",
|
|
143
|
+
"agent doctor",
|
|
144
|
+
write_policy="read_only",
|
|
145
|
+
),
|
|
146
|
+
plan_step(
|
|
147
|
+
"local-automation-factory",
|
|
148
|
+
"Preparar criacao local de scripts, skills, knowledge e agentes personalizados em .agent-devkit.",
|
|
149
|
+
"agent local list",
|
|
150
|
+
write_policy="local_config_write",
|
|
151
|
+
),
|
|
152
|
+
plan_step(
|
|
153
|
+
"tasks-and-notifications",
|
|
154
|
+
"Configurar tarefas agendadas e notificacoes locais com eventos de conclusao/bloqueio.",
|
|
155
|
+
"agent notifications doctor",
|
|
156
|
+
write_policy="local_config_write",
|
|
157
|
+
),
|
|
158
|
+
plan_step(
|
|
159
|
+
"knowledge-and-shared-memory",
|
|
160
|
+
"Inicializar knowledge local e memoria compartilhada com curadoria pelo dono.",
|
|
161
|
+
"agent knowledge doctor",
|
|
162
|
+
write_policy="local_config_write",
|
|
163
|
+
),
|
|
164
|
+
]
|
|
165
|
+
steps = minimal_steps if selected_mode == "minimal" else complete_steps
|
|
166
|
+
return {
|
|
167
|
+
"kind": "onboarding-plan",
|
|
168
|
+
"schema_version": ONBOARDING_MODE_SCHEMA_VERSION,
|
|
169
|
+
"status": "planned",
|
|
170
|
+
"mode": selected_mode,
|
|
171
|
+
"external_actions_executed": False,
|
|
172
|
+
"agent": status.get("agent"),
|
|
173
|
+
"home": status.get("home"),
|
|
174
|
+
"agent_catalog": {
|
|
175
|
+
"agents": len(registry.get("agents") or {}),
|
|
176
|
+
"capabilities": len(registry.get("capabilities") or {}),
|
|
177
|
+
},
|
|
178
|
+
"steps": steps,
|
|
179
|
+
"next_steps": [step["command"] for step in steps],
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def normalize_onboarding_mode(mode: str) -> str:
|
|
184
|
+
if mode in {"minimal", "complete"}:
|
|
185
|
+
return mode
|
|
186
|
+
raise ValueError(f"unsupported onboarding mode: {mode}")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def onboarding_modes() -> list[dict[str, Any]]:
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
"id": "minimal",
|
|
193
|
+
"label": "Onboarding minimo",
|
|
194
|
+
"command": "agent onboard minimal",
|
|
195
|
+
"purpose": "Deixar o agente conversavel e utilizavel com identidade, coordenador LLM, mini-brain e memoria local.",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "complete",
|
|
199
|
+
"label": "Onboarding completo",
|
|
200
|
+
"command": "agent onboard complete",
|
|
201
|
+
"purpose": "Revisar tambem toolchain, providers, fontes, catalogo, automacoes locais, tarefas, notificacoes e memorias compartilhadas.",
|
|
202
|
+
},
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def plan_step(
|
|
207
|
+
step_id: str,
|
|
208
|
+
purpose: str,
|
|
209
|
+
command: str,
|
|
210
|
+
*,
|
|
211
|
+
write_policy: str,
|
|
212
|
+
model: str | None = None,
|
|
213
|
+
) -> dict[str, Any]:
|
|
214
|
+
payload = {
|
|
215
|
+
"id": step_id,
|
|
216
|
+
"status": "planned",
|
|
217
|
+
"purpose": purpose,
|
|
218
|
+
"command": command,
|
|
219
|
+
"write_policy": write_policy,
|
|
220
|
+
"external_write": False,
|
|
221
|
+
}
|
|
222
|
+
if model:
|
|
223
|
+
payload["model"] = model
|
|
224
|
+
return payload
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def summarize_llms() -> dict[str, Any]:
|
|
228
|
+
backends = list_backends()
|
|
229
|
+
doctor = doctor_backends()
|
|
230
|
+
items = doctor.get("items") or []
|
|
231
|
+
usable = [item for item in items if item.get("status") == "ok" and item.get("id") != "ollama"]
|
|
232
|
+
ollama_item = next((item for item in items if item.get("id") == "ollama"), None)
|
|
233
|
+
preference = backends.get("preference") or {}
|
|
234
|
+
return {
|
|
235
|
+
"status": "ok" if usable else "missing",
|
|
236
|
+
"default": backends.get("default"),
|
|
237
|
+
"primary": preference.get("primary"),
|
|
238
|
+
"fallback_enabled": preference.get("fallback_enabled"),
|
|
239
|
+
"configured_count": len([item for item in backends.get("items") or [] if item.get("configured")]),
|
|
240
|
+
"usable_count": len(usable),
|
|
241
|
+
"backends": [
|
|
242
|
+
{
|
|
243
|
+
"id": item.get("id"),
|
|
244
|
+
"status": item.get("status"),
|
|
245
|
+
"configured": item.get("configured"),
|
|
246
|
+
"kind": item.get("kind"),
|
|
247
|
+
}
|
|
248
|
+
for item in items
|
|
249
|
+
],
|
|
250
|
+
"ollama_backend": {
|
|
251
|
+
"status": ollama_item.get("status") if isinstance(ollama_item, dict) else None,
|
|
252
|
+
"configured": ollama_item.get("configured") if isinstance(ollama_item, dict) else None,
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def summarize_ollama(payload: dict[str, Any]) -> dict[str, Any]:
|
|
258
|
+
return {
|
|
259
|
+
"status": payload.get("status"),
|
|
260
|
+
"binary": payload.get("binary"),
|
|
261
|
+
"version": payload.get("version"),
|
|
262
|
+
"daemon": payload.get("daemon"),
|
|
263
|
+
"model_count": payload.get("model_count", 0),
|
|
264
|
+
"install_plan": payload.get("install_plan"),
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def summarize_toolchain(payload: dict[str, Any]) -> dict[str, Any]:
|
|
269
|
+
items = payload.get("items") or []
|
|
270
|
+
return {
|
|
271
|
+
"status": payload.get("status"),
|
|
272
|
+
"platform": payload.get("platform"),
|
|
273
|
+
"required_missing": payload.get("required_missing") or [],
|
|
274
|
+
"optional_missing": payload.get("optional_missing") or [],
|
|
275
|
+
"ok_count": len([item for item in items if item.get("status") == "ok"]),
|
|
276
|
+
"missing_count": len([item for item in items if item.get("status") == "missing"]),
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def summarize_sources() -> dict[str, Any]:
|
|
281
|
+
sources = list_sources()
|
|
282
|
+
try:
|
|
283
|
+
status = source_status()
|
|
284
|
+
except ValueError:
|
|
285
|
+
status = {"status": "missing", "items": []}
|
|
286
|
+
return {
|
|
287
|
+
"status": status.get("status"),
|
|
288
|
+
"count": len(sources.get("items") or []),
|
|
289
|
+
"defaults": sources.get("defaults") or {},
|
|
290
|
+
"stored_secret": bool(sources.get("stored_secret")),
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def summarize_tasks() -> dict[str, Any]:
|
|
295
|
+
tasks = list_tasks()
|
|
296
|
+
raw = load_tasks()
|
|
297
|
+
raw_items = [item for item in raw.get("tasks") or [] if isinstance(item, dict)]
|
|
298
|
+
items = tasks.get("items") or []
|
|
299
|
+
enabled = [item for item in items if item.get("status") == "enabled"]
|
|
300
|
+
due = [public_task(item) for item in raw_items if item.get("status") == "enabled" and task_is_due(item)]
|
|
301
|
+
pending = [item for item in enabled if item.get("run_count", 0) == 0]
|
|
302
|
+
return {
|
|
303
|
+
"status": tasks.get("status"),
|
|
304
|
+
"count": len(items),
|
|
305
|
+
"enabled_count": len(enabled),
|
|
306
|
+
"pending_count": len(pending),
|
|
307
|
+
"due_count": len(due),
|
|
308
|
+
"due": due[:5],
|
|
309
|
+
"pending": pending[:5],
|
|
310
|
+
"path": tasks.get("path"),
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def summarize_specialists(payload: dict[str, Any]) -> dict[str, Any]:
|
|
315
|
+
return {
|
|
316
|
+
"status": payload.get("status"),
|
|
317
|
+
"agents_total": payload.get("agents_total"),
|
|
318
|
+
"capabilities_total": payload.get("capabilities_total"),
|
|
319
|
+
"agents_with_provider_requirements": payload.get("agents_with_provider_requirements"),
|
|
320
|
+
"ready_agents": payload.get("ready_agents"),
|
|
321
|
+
"partial_agents": payload.get("partial_agents"),
|
|
322
|
+
"needs_setup_agents": payload.get("needs_setup_agents"),
|
|
323
|
+
"configured_providers": payload.get("configured_providers") or [],
|
|
324
|
+
"missing_providers": payload.get("missing_providers") or [],
|
|
325
|
+
"next_steps": payload.get("next_steps") or [],
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def startup_flow(
|
|
330
|
+
*,
|
|
331
|
+
status: str,
|
|
332
|
+
sessions: dict[str, Any],
|
|
333
|
+
tasks: dict[str, Any],
|
|
334
|
+
blockers: list[dict[str, str]],
|
|
335
|
+
) -> list[dict[str, str]]:
|
|
336
|
+
steps = [
|
|
337
|
+
{"id": "validate-home", "status": "done", "label": "Validar home local e memoria"},
|
|
338
|
+
{"id": "validate-setup", "status": "attention" if blockers else "done", "label": "Validar setup minimo"},
|
|
339
|
+
{
|
|
340
|
+
"id": "check-session",
|
|
341
|
+
"status": "active" if sessions.get("active") else "empty",
|
|
342
|
+
"label": "Verificar conversa ou analise em andamento",
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"id": "check-tasks",
|
|
346
|
+
"status": "due" if tasks.get("due_count") else ("pending" if tasks.get("pending_count") else "empty"),
|
|
347
|
+
"label": "Verificar tarefas pendentes e dados a retornar",
|
|
348
|
+
},
|
|
349
|
+
{"id": "ask-next-action", "status": status, "label": "Responder ou perguntar o proximo objetivo"},
|
|
350
|
+
]
|
|
351
|
+
return steps
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def assistant_prompt(
|
|
355
|
+
*,
|
|
356
|
+
status: str,
|
|
357
|
+
sessions: dict[str, Any],
|
|
358
|
+
tasks: dict[str, Any],
|
|
359
|
+
blockers: list[dict[str, str]],
|
|
360
|
+
actions: list[dict[str, str]],
|
|
361
|
+
) -> str:
|
|
362
|
+
if blockers:
|
|
363
|
+
return "Preciso concluir o setup minimo antes de executar tarefas. Posso iniciar o onboarding minimo ou completo."
|
|
364
|
+
if tasks.get("due_count"):
|
|
365
|
+
return "Encontrei tarefas agendadas prontas para revisao. Posso listar, executar em dry-run ou aguardar sua instrucao."
|
|
366
|
+
if tasks.get("pending_count"):
|
|
367
|
+
pending = tasks.get("pending") if isinstance(tasks.get("pending"), list) else []
|
|
368
|
+
first = pending[0] if pending and isinstance(pending[0], dict) else {}
|
|
369
|
+
title = first.get("title") or first.get("id") or "tarefa pendente"
|
|
370
|
+
active = sessions.get("active") if isinstance(sessions.get("active"), dict) else None
|
|
371
|
+
if active:
|
|
372
|
+
session_title = active.get("title") or active.get("id")
|
|
373
|
+
return f"Existe uma sessao ativa: {session_title}. Tambem encontrei tarefa pendente: {title}. Posso continuar a conversa, listar tarefas ou executar a pendencia em dry-run."
|
|
374
|
+
return f"Encontrei tarefa pendente: {title}. Posso listar, executar em dry-run ou receber uma nova tarefa."
|
|
375
|
+
active = sessions.get("active") if isinstance(sessions.get("active"), dict) else None
|
|
376
|
+
if active:
|
|
377
|
+
title = active.get("title") or active.get("id")
|
|
378
|
+
return f"Existe uma sessao ativa: {title}. Posso continuar essa conversa, mostrar status ou iniciar uma nova tarefa."
|
|
379
|
+
if actions:
|
|
380
|
+
return "Estou pronto. Posso continuar o onboarding, configurar ferramentas sob demanda ou receber uma tarefa em linguagem natural."
|
|
381
|
+
return "Estou pronto. O que voce quer fazer agora?"
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def startup_blockers(*, setup: dict[str, Any], llms: dict[str, Any]) -> list[dict[str, str]]:
|
|
385
|
+
blockers: list[dict[str, str]] = []
|
|
386
|
+
toolchain = setup.get("toolchain") or {}
|
|
387
|
+
for item in toolchain.get("required_missing") or []:
|
|
388
|
+
blockers.append(
|
|
389
|
+
{
|
|
390
|
+
"id": f"toolchain.{item}",
|
|
391
|
+
"message": f"Required toolchain item is missing: {item}.",
|
|
392
|
+
"command": f"agent toolchain install {item} --dry-run",
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
if llms.get("usable_count", 0) < 1:
|
|
396
|
+
blockers.append(
|
|
397
|
+
{
|
|
398
|
+
"id": "llm.unavailable",
|
|
399
|
+
"message": "No usable coordinator LLM backend was detected.",
|
|
400
|
+
"command": "agent llm doctor",
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
return blockers
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def suggested_actions(
|
|
407
|
+
*,
|
|
408
|
+
memory: dict[str, Any],
|
|
409
|
+
personality: dict[str, Any],
|
|
410
|
+
sessions: dict[str, Any],
|
|
411
|
+
setup: dict[str, Any],
|
|
412
|
+
llms: dict[str, Any],
|
|
413
|
+
ollama: dict[str, Any],
|
|
414
|
+
sources: dict[str, Any],
|
|
415
|
+
tasks: dict[str, Any],
|
|
416
|
+
specialists: dict[str, Any],
|
|
417
|
+
) -> list[dict[str, str]]:
|
|
418
|
+
actions: list[dict[str, str]] = []
|
|
419
|
+
due_tasks = tasks.get("due") if isinstance(tasks.get("due"), list) else []
|
|
420
|
+
pending_tasks = tasks.get("pending") if isinstance(tasks.get("pending"), list) else []
|
|
421
|
+
first_due = due_tasks[0] if due_tasks and isinstance(due_tasks[0], dict) else None
|
|
422
|
+
first_pending = pending_tasks[0] if pending_tasks and isinstance(pending_tasks[0], dict) else None
|
|
423
|
+
if first_due and first_due.get("id"):
|
|
424
|
+
actions.append(
|
|
425
|
+
{
|
|
426
|
+
"id": "tasks.run-due",
|
|
427
|
+
"label": f"Run due task: {first_due.get('title') or first_due.get('id')}",
|
|
428
|
+
"command": f"agent task run {first_due['id']} --dry-run",
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
if first_pending and first_pending.get("id"):
|
|
432
|
+
actions.append(
|
|
433
|
+
{
|
|
434
|
+
"id": "tasks.run-pending",
|
|
435
|
+
"label": f"Run pending task: {first_pending.get('title') or first_pending.get('id')}",
|
|
436
|
+
"command": f"agent task run {first_pending['id']} --dry-run",
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
actions.append(
|
|
440
|
+
{
|
|
441
|
+
"id": "tasks.review",
|
|
442
|
+
"label": "Review local scheduled and manual tasks",
|
|
443
|
+
"command": "agent task list",
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
if memory.get("created"):
|
|
447
|
+
actions.append(
|
|
448
|
+
{
|
|
449
|
+
"id": "memory.review",
|
|
450
|
+
"label": "Review local memory files",
|
|
451
|
+
"command": "agent memory show",
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
if not personality.get("user_name"):
|
|
455
|
+
actions.append(
|
|
456
|
+
{
|
|
457
|
+
"id": "personality.setup",
|
|
458
|
+
"label": "Configure agent name, user name and response style",
|
|
459
|
+
"command": "agent setup personality",
|
|
460
|
+
}
|
|
461
|
+
)
|
|
462
|
+
if llms.get("usable_count", 0) < 1:
|
|
463
|
+
actions.append(
|
|
464
|
+
{
|
|
465
|
+
"id": "llm.configure",
|
|
466
|
+
"label": "Configure a coordinator LLM backend",
|
|
467
|
+
"command": "agent llm doctor",
|
|
468
|
+
}
|
|
469
|
+
)
|
|
470
|
+
if ollama.get("status") == "missing":
|
|
471
|
+
actions.append(
|
|
472
|
+
{
|
|
473
|
+
"id": "ollama.install",
|
|
474
|
+
"label": "Inspect local Ollama mini-brain setup",
|
|
475
|
+
"command": "agent ollama status",
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
if (setup.get("toolchain") or {}).get("optional_missing"):
|
|
479
|
+
actions.append(
|
|
480
|
+
{
|
|
481
|
+
"id": "toolchain.optional",
|
|
482
|
+
"label": "Review optional local tools",
|
|
483
|
+
"command": "agent toolchain doctor",
|
|
484
|
+
}
|
|
485
|
+
)
|
|
486
|
+
if sources.get("count", 0) < 1:
|
|
487
|
+
actions.append(
|
|
488
|
+
{
|
|
489
|
+
"id": "sources.configure",
|
|
490
|
+
"label": "Add reusable project/provider sources when needed",
|
|
491
|
+
"command": "agent source list",
|
|
492
|
+
}
|
|
493
|
+
)
|
|
494
|
+
if specialists.get("status") in {"needs-setup", "partial"}:
|
|
495
|
+
next_steps = specialists.get("next_steps") if isinstance(specialists.get("next_steps"), list) else []
|
|
496
|
+
actions.append(
|
|
497
|
+
{
|
|
498
|
+
"id": "specialists.readiness",
|
|
499
|
+
"label": "Review specialist agent provider readiness",
|
|
500
|
+
"command": str(next_steps[0] if next_steps else "agent doctor"),
|
|
501
|
+
}
|
|
502
|
+
)
|
|
503
|
+
if tasks.get("count", 0) < 1:
|
|
504
|
+
actions.append(
|
|
505
|
+
{
|
|
506
|
+
"id": "tasks.create",
|
|
507
|
+
"label": "Create scheduled or recurring tasks when useful",
|
|
508
|
+
"command": "agent task list",
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
if not sessions.get("active_session_id"):
|
|
512
|
+
actions.append(
|
|
513
|
+
{
|
|
514
|
+
"id": "session.start",
|
|
515
|
+
"label": "Start by sending a natural-language task",
|
|
516
|
+
"command": "agent \"o que voce consegue fazer aqui?\"",
|
|
517
|
+
}
|
|
518
|
+
)
|
|
519
|
+
return dedupe_actions(actions)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def dedupe_actions(actions: list[dict[str, str]]) -> list[dict[str, str]]:
|
|
523
|
+
seen: set[str] = set()
|
|
524
|
+
deduped: list[dict[str, str]] = []
|
|
525
|
+
for action in actions:
|
|
526
|
+
action_id = action.get("id")
|
|
527
|
+
if not action_id or action_id in seen:
|
|
528
|
+
continue
|
|
529
|
+
seen.add(action_id)
|
|
530
|
+
deduped.append(action)
|
|
531
|
+
return deduped
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def summarize_sessions() -> dict[str, Any]:
|
|
535
|
+
sessions = list_sessions()
|
|
536
|
+
active_id = sessions.get("active_session_id")
|
|
537
|
+
active = None
|
|
538
|
+
if active_id:
|
|
539
|
+
try:
|
|
540
|
+
payload = show_session(str(active_id))
|
|
541
|
+
active = payload.get("session")
|
|
542
|
+
if isinstance(active, dict):
|
|
543
|
+
active["summary"] = payload.get("summary")
|
|
544
|
+
active["recent_messages"] = payload.get("recent_messages") or []
|
|
545
|
+
except ValueError:
|
|
546
|
+
active = None
|
|
547
|
+
return {
|
|
548
|
+
**sessions,
|
|
549
|
+
"active": active,
|
|
550
|
+
"recent": (sessions.get("items") or [])[:5],
|
|
551
|
+
}
|