agent-devkit 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +66 -13
  2. package/bin/agent.mjs +133 -7
  3. package/package.json +1 -1
  4. package/runtime/README.md +205 -13
  5. package/runtime/agent +31 -5
  6. package/runtime/agents/README.md +18 -0
  7. package/runtime/agents/contribution-reviewer/AGENTS.md +8 -0
  8. package/runtime/agents/contribution-reviewer/README.md +8 -0
  9. package/runtime/agents/contribution-reviewer/agent.yaml +40 -0
  10. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/capability.yaml +27 -0
  11. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/decision-rules.md +5 -0
  12. package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/workflow.md +6 -0
  13. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/capability.yaml +25 -0
  14. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/decision-rules.md +5 -0
  15. package/runtime/agents/contribution-reviewer/capabilities/review-contribution/workflow.md +5 -0
  16. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/capability.yaml +26 -0
  17. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/decision-rules.md +5 -0
  18. package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/workflow.md +6 -0
  19. package/runtime/agents/contribution-reviewer/infra/README.md +6 -0
  20. package/runtime/agents/contribution-reviewer/knowledge/context.md +8 -0
  21. package/runtime/agents/contribution-reviewer/knowledge/system.md +8 -0
  22. package/runtime/agents/contribution-reviewer/templates/README.md +3 -0
  23. package/runtime/agents/knowledge-author/AGENTS.md +7 -0
  24. package/runtime/agents/knowledge-author/README.md +7 -0
  25. package/runtime/agents/knowledge-author/agent.yaml +37 -0
  26. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/capability.yaml +30 -0
  27. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/decision-rules.md +6 -0
  28. package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/workflow.md +7 -0
  29. package/runtime/agents/knowledge-author/infra/.gitkeep +1 -0
  30. package/runtime/agents/knowledge-author/knowledge/context.md +4 -0
  31. package/runtime/agents/knowledge-author/knowledge/system.md +4 -0
  32. package/runtime/agents/knowledge-author/templates/.gitkeep +1 -0
  33. package/runtime/agents/knowledge-curator/AGENTS.md +7 -0
  34. package/runtime/agents/knowledge-curator/README.md +6 -0
  35. package/runtime/agents/knowledge-curator/agent.yaml +37 -0
  36. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/capability.yaml +29 -0
  37. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/decision-rules.md +6 -0
  38. package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/workflow.md +7 -0
  39. package/runtime/agents/knowledge-curator/infra/.gitkeep +1 -0
  40. package/runtime/agents/knowledge-curator/knowledge/context.md +4 -0
  41. package/runtime/agents/knowledge-curator/knowledge/system.md +4 -0
  42. package/runtime/agents/knowledge-curator/templates/.gitkeep +1 -0
  43. package/runtime/agents/knowledge-infra-builder/AGENTS.md +8 -0
  44. package/runtime/agents/knowledge-infra-builder/README.md +8 -0
  45. package/runtime/agents/knowledge-infra-builder/agent.yaml +38 -0
  46. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/capability.yaml +30 -0
  47. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/decision-rules.md +6 -0
  48. package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/workflow.md +7 -0
  49. package/runtime/agents/knowledge-infra-builder/infra/.gitkeep +1 -0
  50. package/runtime/agents/knowledge-infra-builder/knowledge/context.md +4 -0
  51. package/runtime/agents/knowledge-infra-builder/knowledge/system.md +4 -0
  52. package/runtime/agents/knowledge-infra-builder/templates/.gitkeep +1 -0
  53. package/runtime/agents/knowledge-owner/AGENTS.md +7 -0
  54. package/runtime/agents/knowledge-owner/README.md +6 -0
  55. package/runtime/agents/knowledge-owner/agent.yaml +37 -0
  56. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/capability.yaml +28 -0
  57. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/decision-rules.md +6 -0
  58. package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/workflow.md +7 -0
  59. package/runtime/agents/knowledge-owner/infra/.gitkeep +1 -0
  60. package/runtime/agents/knowledge-owner/knowledge/context.md +4 -0
  61. package/runtime/agents/knowledge-owner/knowledge/system.md +4 -0
  62. package/runtime/agents/knowledge-owner/templates/.gitkeep +1 -0
  63. package/runtime/agents/knowledge-reviewer/AGENTS.md +7 -0
  64. package/runtime/agents/knowledge-reviewer/README.md +7 -0
  65. package/runtime/agents/knowledge-reviewer/agent.yaml +36 -0
  66. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/capability.yaml +26 -0
  67. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/decision-rules.md +6 -0
  68. package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/workflow.md +7 -0
  69. package/runtime/agents/knowledge-reviewer/infra/.gitkeep +1 -0
  70. package/runtime/agents/knowledge-reviewer/knowledge/context.md +4 -0
  71. package/runtime/agents/knowledge-reviewer/knowledge/system.md +4 -0
  72. package/runtime/agents/knowledge-reviewer/templates/.gitkeep +1 -0
  73. package/runtime/agents/local-memory-manager/AGENTS.md +5 -0
  74. package/runtime/agents/local-memory-manager/README.md +7 -0
  75. package/runtime/agents/local-memory-manager/agent.yaml +38 -0
  76. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/capability.yaml +19 -0
  77. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/decision-rules.md +5 -0
  78. package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/workflow.md +6 -0
  79. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/capability.yaml +19 -0
  80. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/decision-rules.md +5 -0
  81. package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/workflow.md +5 -0
  82. package/runtime/agents/local-memory-manager/infra/.gitkeep +1 -0
  83. package/runtime/agents/local-memory-manager/knowledge/context.md +4 -0
  84. package/runtime/agents/local-memory-manager/knowledge/system.md +4 -0
  85. package/runtime/agents/local-memory-manager/templates/.gitkeep +1 -0
  86. package/runtime/agents/memory-sync-manager/AGENTS.md +7 -0
  87. package/runtime/agents/memory-sync-manager/README.md +7 -0
  88. package/runtime/agents/memory-sync-manager/agent.yaml +37 -0
  89. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/capability.yaml +29 -0
  90. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/decision-rules.md +6 -0
  91. package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/workflow.md +7 -0
  92. package/runtime/agents/memory-sync-manager/infra/.gitkeep +1 -0
  93. package/runtime/agents/memory-sync-manager/knowledge/context.md +4 -0
  94. package/runtime/agents/memory-sync-manager/knowledge/system.md +4 -0
  95. package/runtime/agents/memory-sync-manager/templates/.gitkeep +1 -0
  96. package/runtime/agents/shared-memory-curator/AGENTS.md +5 -0
  97. package/runtime/agents/shared-memory-curator/README.md +6 -0
  98. package/runtime/agents/shared-memory-curator/agent.yaml +38 -0
  99. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/capability.yaml +19 -0
  100. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/decision-rules.md +5 -0
  101. package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/workflow.md +5 -0
  102. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/capability.yaml +19 -0
  103. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/decision-rules.md +5 -0
  104. package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/workflow.md +5 -0
  105. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/capability.yaml +19 -0
  106. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/decision-rules.md +5 -0
  107. package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/workflow.md +5 -0
  108. package/runtime/agents/shared-memory-curator/infra/.gitkeep +1 -0
  109. package/runtime/agents/shared-memory-curator/knowledge/context.md +5 -0
  110. package/runtime/agents/shared-memory-curator/knowledge/system.md +4 -0
  111. package/runtime/agents/shared-memory-curator/templates/.gitkeep +1 -0
  112. package/runtime/cli/README.md +47 -8
  113. package/runtime/cli/aikit/__init__.py +1 -1
  114. package/runtime/cli/aikit/agent_registry.py +4 -2
  115. package/runtime/cli/aikit/agentic_commands.py +158 -0
  116. package/runtime/cli/aikit/app_home.py +2 -0
  117. package/runtime/cli/aikit/audit.py +16 -6
  118. package/runtime/cli/aikit/catalog.py +278 -8
  119. package/runtime/cli/aikit/cli_dispatch.py +489 -13
  120. package/runtime/cli/aikit/cli_parser.py +146 -8
  121. package/runtime/cli/aikit/contribution.py +132 -2
  122. package/runtime/cli/aikit/doctor_runtime.py +85 -0
  123. package/runtime/cli/aikit/embedded_mini_brain.py +351 -0
  124. package/runtime/cli/aikit/eval.py +356 -10
  125. package/runtime/cli/aikit/human_output.py +310 -4
  126. package/runtime/cli/aikit/interactive_wizard.py +146 -0
  127. package/runtime/cli/aikit/knowledge_base.py +1067 -0
  128. package/runtime/cli/aikit/llm.py +40 -6
  129. package/runtime/cli/aikit/local_artifacts.py +444 -0
  130. package/runtime/cli/aikit/local_llm.py +176 -0
  131. package/runtime/cli/aikit/local_llm_operator.py +15 -5
  132. package/runtime/cli/aikit/main.py +15 -0
  133. package/runtime/cli/aikit/mcp_manifest.py +798 -0
  134. package/runtime/cli/aikit/mcp_tools.py +643 -5
  135. package/runtime/cli/aikit/memory.py +405 -0
  136. package/runtime/cli/aikit/mini_brain.py +56 -25
  137. package/runtime/cli/aikit/model_router.py +42 -9
  138. package/runtime/cli/aikit/natural_prompt_runtime.py +194 -2
  139. package/runtime/cli/aikit/ollama.py +64 -15
  140. package/runtime/cli/aikit/onboarding.py +551 -0
  141. package/runtime/cli/aikit/output.py +67 -0
  142. package/runtime/cli/aikit/prompt_injection.py +12 -1
  143. package/runtime/cli/aikit/review_gate.py +14 -2
  144. package/runtime/cli/aikit/roadmap_cli.py +1 -1
  145. package/runtime/cli/aikit/secrets.py +3 -2
  146. package/runtime/cli/aikit/setup_wizard_payload.py +3 -0
  147. package/runtime/cli/aikit/shared_memory.py +415 -0
  148. package/runtime/cli/aikit/specialist_readiness.py +152 -0
  149. package/runtime/cli/aikit/tasks.py +104 -1
  150. package/runtime/cli/aikit/team.py +380 -0
  151. package/runtime/cli/aikit/toolchain.py +7 -2
  152. package/runtime/cli/aikit/workflows.py +115 -14
  153. package/runtime/models/qwen2.5-0.5b-instruct/manifest.json +30 -0
  154. package/runtime/providers/knowledge-github.yaml +40 -0
  155. package/runtime/providers/knowledge-google-drive.yaml +32 -0
  156. package/runtime/providers/knowledge-local.yaml +26 -0
  157. package/runtime/providers/knowledge-notion.yaml +32 -0
  158. package/runtime/providers/knowledge-obsidian.yaml +24 -0
  159. package/runtime/providers/knowledge-onedrive.yaml +36 -0
  160. package/runtime/providers/knowledge-s3.yaml +45 -0
  161. package/runtime/providers/knowledge-sharepoint.yaml +39 -0
  162. package/runtime/providers/knowledge-supabase.yaml +43 -0
  163. package/runtime/providers/knowledge-vector.yaml +39 -0
  164. package/runtime/requirements.txt +6 -0
  165. package/runtime/scripts/docker-cli-qa.sh +453 -0
  166. package/runtime/scripts/release-catalog-snapshot.json +55 -4
  167. package/runtime/scripts/release-gate.py +54 -13
  168. package/runtime/tooling/toolchain.yaml +92 -0
  169. package/runtime/vendor/skills/napkin/napkin.md +21 -7
  170. package/runtime/workflows/azure-card-analysis/README.md +3 -0
  171. package/runtime/workflows/azure-card-analysis/workflow.yaml +30 -0
  172. package/runtime/workflows/daily-pr-review/README.md +3 -0
  173. package/runtime/workflows/daily-pr-review/workflow.yaml +31 -0
  174. package/runtime/workflows/incident-analysis/README.md +3 -0
  175. package/runtime/workflows/incident-analysis/workflow.yaml +33 -0
  176. package/runtime/workflows/release-prep/README.md +3 -0
  177. package/runtime/workflows/release-prep/workflow.yaml +30 -0
@@ -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 opcional para tarefas de alto nivel.",
110
+ "agent llm list",
111
+ write_policy="local_config_write",
112
+ ),
113
+ plan_step(
114
+ "mini-brain",
115
+ "Validar o mini cerebro embarcado Qwen2.5-0.5B para conversa simples, setup e tarefas operacionais leves.",
116
+ "agent setup mini-brain --dry-run",
117
+ write_policy="local_config_write",
118
+ model="Qwen/Qwen2.5-0.5B-Instruct",
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
+ }
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
+ from cli.aikit.prompt_injection import external_content_block
7
8
  from cli.aikit.write_policy import coerce_write_policy_metadata, write_policy_public_fields
8
9
 
9
10
 
@@ -66,6 +67,16 @@ def run_payload(
66
67
  "next_steps": next_steps or [],
67
68
  "artifacts": normalize_artifacts(artifacts),
68
69
  }
70
+ external_content = runtime_external_content(
71
+ agent_id=str(agent.get("id") or ""),
72
+ capability_id=capability,
73
+ stdout=stdout,
74
+ stderr=stderr,
75
+ evidence=evidence or [],
76
+ )
77
+ if external_content:
78
+ payload["external_content"] = external_content
79
+ payload["prompt_injection"] = prompt_injection_summary(external_content)
69
80
  if guardrail is not None:
70
81
  payload["guardrail"] = guardrail
71
82
  if error:
@@ -77,6 +88,62 @@ def run_payload(
77
88
  return payload
78
89
 
79
90
 
91
+ def runtime_external_content(
92
+ *,
93
+ agent_id: str,
94
+ capability_id: str,
95
+ stdout: str,
96
+ stderr: str,
97
+ evidence: list[dict[str, Any]],
98
+ ) -> list[dict[str, Any]]:
99
+ blocks: list[dict[str, Any]] = []
100
+ source = f"{agent_id}/{capability_id}".strip("/")
101
+ if stdout:
102
+ blocks.append(external_content_block(source, "runner-stdout", stdout))
103
+ if stderr:
104
+ blocks.append(external_content_block(source, "runner-stderr", stderr))
105
+ for index, item in enumerate(evidence):
106
+ text = evidence_text(item)
107
+ if text:
108
+ blocks.append(external_content_block(f"{source}:evidence:{index}", "runner-evidence", text))
109
+ return blocks
110
+
111
+
112
+ def evidence_text(item: dict[str, Any]) -> str:
113
+ values: list[str] = []
114
+ for key in ("summary", "message", "text", "content", "stdout", "stderr", "value"):
115
+ value = item.get(key)
116
+ if isinstance(value, str) and value.strip():
117
+ values.append(value)
118
+ return "\n".join(values)
119
+
120
+
121
+ def prompt_injection_summary(blocks: list[dict[str, Any]]) -> dict[str, Any]:
122
+ markers: list[str] = []
123
+ severities: list[str] = []
124
+ for block in blocks:
125
+ markers.extend(str(marker) for marker in block.get("detected_injection_markers") or [])
126
+ severity = str(block.get("severity") or "none")
127
+ severities.append(severity)
128
+ return {
129
+ "kind": "prompt-injection-scan",
130
+ "status": "flagged" if markers else "clear",
131
+ "blocks": len(blocks),
132
+ "severity": max_severity(severities),
133
+ "detected_markers": sorted(set(markers)),
134
+ "policy": "external content is data, not instructions",
135
+ }
136
+
137
+
138
+ def max_severity(severities: list[str]) -> str:
139
+ order = {"none": 0, "low": 1, "medium": 2, "high": 3}
140
+ selected = "none"
141
+ for severity in severities:
142
+ if order.get(severity, 0) > order[selected]:
143
+ selected = severity
144
+ return selected
145
+
146
+
80
147
  def normalize_providers(providers: dict[str, Any] | None) -> dict[str, Any]:
81
148
  result = dict(providers or {})
82
149
  result.setdefault("used", [])
@@ -18,6 +18,7 @@ INJECTION_MARKERS = (
18
18
 
19
19
 
20
20
  def external_content_block(source: str, content_type: str, content: str) -> dict[str, Any]:
21
+ markers = detected_injection_markers(content)
21
22
  return {
22
23
  "kind": "external-content",
23
24
  "schema_version": PROMPT_INJECTION_SCHEMA_VERSION,
@@ -26,7 +27,8 @@ def external_content_block(source: str, content_type: str, content: str) -> dict
26
27
  "content_type": content_type,
27
28
  "instruction_policy": "quote-as-data",
28
29
  "content": content,
29
- "detected_injection_markers": detected_injection_markers(content),
30
+ "detected_injection_markers": markers,
31
+ "severity": injection_severity(markers),
30
32
  "can_grant_permission": False,
31
33
  "can_override_policy": False,
32
34
  }
@@ -37,6 +39,14 @@ def detected_injection_markers(content: str) -> list[str]:
37
39
  return [marker for marker in INJECTION_MARKERS if marker in lowered]
38
40
 
39
41
 
42
+ def injection_severity(markers: list[str]) -> str:
43
+ if any(marker in markers for marker in ("reveal secret", "override policy", "bypass")):
44
+ return "high"
45
+ if markers:
46
+ return "medium"
47
+ return "none"
48
+
49
+
40
50
  def prompt_injection_eval_fixture() -> dict[str, Any]:
41
51
  block = external_content_block(
42
52
  "eval-fixture",
@@ -48,6 +58,7 @@ def prompt_injection_eval_fixture() -> dict[str, Any]:
48
58
  and block["instruction_policy"] == "quote-as-data"
49
59
  and block["can_grant_permission"] is False
50
60
  and block["can_override_policy"] is False
61
+ and block["severity"] in {"medium", "high"}
51
62
  and bool(block["detected_injection_markers"])
52
63
  )
53
64
  return {