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
@@ -9,8 +9,10 @@ DETERMINISTIC_COMMANDS = (
9
9
  "architecture",
10
10
  "roadmap",
11
11
  "catalog",
12
+ "plan",
12
13
  "route",
13
14
  "eval",
15
+ "secret",
14
16
  "secrets",
15
17
  "agents",
16
18
  "capabilities",
@@ -18,12 +20,14 @@ DETERMINISTIC_COMMANDS = (
18
20
  "run",
19
21
  "doctor",
20
22
  "commands",
23
+ "onboard",
21
24
  "llm",
22
25
  "providers",
23
26
  "provider",
24
27
  "credential",
25
28
  "source",
26
29
  "memory",
30
+ "shared-memory",
27
31
  "personality",
28
32
  "setup",
29
33
  "alias",
@@ -40,17 +44,23 @@ DETERMINISTIC_COMMANDS = (
40
44
  "tools",
41
45
  "integrations",
42
46
  "skills",
47
+ "skill",
48
+ "script",
43
49
  "decisions",
44
50
  "ollama",
51
+ "local-llm",
45
52
  "mcp",
46
53
  "local",
47
54
  "workflow",
55
+ "team",
56
+ "knowledge",
57
+ "knowledge-base",
48
58
  "contribute",
49
59
  "contribution",
50
60
  "install",
51
61
  "wizard",
52
62
  )
53
- LLM_COMMANDS = ("agent",)
63
+ LLM_COMMANDS = ("agent", "execute", "orchestrate")
54
64
 
55
65
 
56
66
  def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
@@ -87,6 +97,10 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
87
97
  commands_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
88
98
  commands_parser.add_argument("action", nargs="?", default="list", choices=["list"])
89
99
 
100
+ onboard_parser = subparsers.add_parser("onboard", help="inspect startup state and guide first use")
101
+ onboard_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
102
+ onboard_parser.add_argument("action", nargs="?", default="show", choices=["show", "minimal", "complete"])
103
+
90
104
  architecture_parser = subparsers.add_parser("architecture", help="show the Agent DevKit architecture contract")
91
105
  architecture_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
92
106
  architecture_parser.add_argument("action", nargs="?", default="show", choices=["show"])
@@ -98,8 +112,28 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
98
112
 
99
113
  catalog_parser = subparsers.add_parser("catalog", help="search and inspect the Agent DevKit catalog")
100
114
  catalog_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
101
- catalog_parser.add_argument("action", nargs="?", default="list", choices=["list", "search", "show"])
115
+ catalog_parser.add_argument("action", nargs="?", default="list", choices=["list", "search", "show", "inspect", "rebuild-index"])
102
116
  catalog_parser.add_argument("query", nargs="?")
117
+ catalog_parser.add_argument("target", nargs="?")
118
+ catalog_parser.add_argument("--type", dest="item_type")
119
+ catalog_parser.add_argument("--provider")
120
+ catalog_parser.add_argument("--status")
121
+ catalog_parser.add_argument("--write-policy")
122
+ catalog_parser.add_argument("--readiness")
123
+
124
+ for command_name, help_text in (
125
+ ("plan", "build an explicit agentic execution plan without executing it"),
126
+ ("execute", "execute a natural-language task through the agentic runtime"),
127
+ ("orchestrate", "orchestrate a natural-language task with planning and review gates"),
128
+ ):
129
+ agentic_parser = subparsers.add_parser(command_name, help=help_text)
130
+ agentic_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
131
+ agentic_parser.add_argument("--llm", help="LLM backend id to use")
132
+ agentic_parser.add_argument("--dry-run", action="store_true", help="show execution plan without invoking LLM or external writes")
133
+ agentic_parser.add_argument("--no-llm-fallback", action="store_true", help="disable automatic fallback to secondary LLM backends")
134
+ agentic_parser.add_argument("--session", dest="session_id", help="resume a local conversation session")
135
+ agentic_parser.add_argument("--new-session", action="store_true", help="start a new local conversation session")
136
+ agentic_parser.add_argument("prompt", nargs=argparse.REMAINDER)
103
137
 
104
138
  route_parser = subparsers.add_parser("route", help="explain deterministic routing without execution")
105
139
  route_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
@@ -119,6 +153,14 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
119
153
  secrets_parser.add_argument("key", nargs="?")
120
154
  secrets_parser.add_argument("--env", dest="env")
121
155
 
156
+ secret_parser = subparsers.add_parser("secret", help="manage safe secret references")
157
+ secret_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
158
+ secret_parser.add_argument("action", nargs="?", default="doctor", choices=["set", "get", "list", "delete", "doctor"])
159
+ secret_parser.add_argument("provider", nargs="?")
160
+ secret_parser.add_argument("key", nargs="?")
161
+ secret_parser.add_argument("--env", dest="env")
162
+ secret_parser.add_argument("--ref", dest="secret_ref")
163
+
122
164
  providers_parser = subparsers.add_parser("providers", help="list provider registry entries")
123
165
  providers_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
124
166
  providers_parser.add_argument("action", nargs="?", default="list", choices=["list"])
@@ -159,20 +201,42 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
159
201
  wizard_parser.add_argument("--status", help="filter wizard list by status")
160
202
  wizard_parser.add_argument("--no-run", action="store_true", help="do not resume the original prompt after completing a wizard")
161
203
 
162
- memory_parser = subparsers.add_parser("memory", help="inspect or reset local AI DevKit memory")
204
+ memory_parser = subparsers.add_parser("memory", help="inspect, backup or reset local AI DevKit memory")
163
205
  memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
164
- memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset"])
206
+ memory_parser.add_argument("action", nargs="?", default="show", choices=["show", "path", "reset", "backup", "share", "shared", "read", "submit", "review", "publish"])
207
+ memory_parser.add_argument("memory_id", nargs="?")
208
+ memory_parser.add_argument("submission_id", nargs="?")
165
209
  memory_parser.add_argument("--agent", dest="agent_id")
166
210
  memory_parser.add_argument("--source", dest="source_id")
211
+ memory_parser.add_argument("--title")
212
+ memory_parser.add_argument("--content")
213
+ memory_parser.add_argument("--key", dest="contributor_key")
214
+ memory_parser.add_argument("--owner-key", dest="owner_key")
215
+ memory_parser.add_argument("--encrypted", action="store_true", help="create an encrypted portable memory backup package")
216
+ memory_parser.add_argument("--passphrase-env", help="environment variable that contains the memory backup passphrase")
217
+ memory_parser.add_argument("--file", dest="backup_file", help="restore a portable memory backup package")
167
218
  memory_parser.add_argument("--all", action="store_true", help="reset all local memory")
168
219
  memory_parser.add_argument("--sessions", action="store_true", help="reset local conversation sessions")
169
220
  memory_parser.add_argument("--tasks", action="store_true", help="reset local task schedules")
170
221
  memory_parser.add_argument("--cache", action="store_true", help="reset local cache")
222
+ memory_parser.add_argument("--yes", action="store_true")
223
+
224
+ shared_memory_parser = subparsers.add_parser("shared-memory", help="manage owner-reviewed shared memories")
225
+ shared_memory_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
226
+ shared_memory_parser.add_argument("action", nargs="?", default="list", choices=["create", "list", "status", "read", "submit", "review", "publish"])
227
+ shared_memory_parser.add_argument("memory_id", nargs="?")
228
+ shared_memory_parser.add_argument("submission_id", nargs="?")
229
+ shared_memory_parser.add_argument("--title")
230
+ shared_memory_parser.add_argument("--content")
231
+ shared_memory_parser.add_argument("--key", dest="contributor_key")
232
+ shared_memory_parser.add_argument("--owner-key", dest="owner_key")
233
+ shared_memory_parser.add_argument("--yes", action="store_true")
171
234
 
172
235
  personality_parser = subparsers.add_parser("personality", help="inspect or update local Agent DevKit personality")
173
236
  personality_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
174
237
  personality_parser.add_argument("action", nargs="?", default="show", choices=["show", "edit", "reset"])
175
238
  personality_parser.add_argument("--name", dest="agent_name", help="public agent name")
239
+ personality_parser.add_argument("--rename", dest="agent_name", help="rename the local agent")
176
240
  personality_parser.add_argument("--user-name", help="user name")
177
241
  personality_parser.add_argument("--language", help="default response language")
178
242
  personality_parser.add_argument("--tone", help="response tone")
@@ -182,7 +246,7 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
182
246
  setup_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
183
247
  setup_parser.add_argument("--dry-run", action="store_true", help="show setup plan without installing external tools")
184
248
  setup_parser.add_argument("--yes", action="store_true", help="confirm setup actions")
185
- setup_parser.add_argument("--set-default", action="store_true", help="make the mini-brain Ollama backend the default LLM")
249
+ setup_parser.add_argument("--set-default", action="store_true", help="make the embedded mini-brain the default LLM")
186
250
  setup_parser.add_argument("action", nargs="?", default="plan", choices=["plan", "personality", "mini-brain"])
187
251
 
188
252
  alias_parser = subparsers.add_parser("alias", help="manage local command aliases for agent")
@@ -310,6 +374,13 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
310
374
  ollama_parser.add_argument("--yes", action="store_true", help="confirm Ollama model or update operation")
311
375
  ollama_parser.add_argument("--dry-run", action="store_true", help="show Ollama operation without executing it")
312
376
 
377
+ local_llm_parser = subparsers.add_parser("local-llm", help="inspect and manage local LLM workers")
378
+ local_llm_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
379
+ local_llm_parser.add_argument("action", nargs="?", default="list", choices=["list", "doctor", "models", "install", "remove", "benchmark"])
380
+ local_llm_parser.add_argument("model", nargs="?")
381
+ local_llm_parser.add_argument("--yes", action="store_true", help="confirm local model operation")
382
+ local_llm_parser.add_argument("--dry-run", action="store_true", help="show local model operation without executing it")
383
+
313
384
  llm_parser = subparsers.add_parser("llm", help="manage LLM backends")
314
385
  llm_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
315
386
  llm_parser.add_argument(
@@ -344,8 +415,10 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
344
415
 
345
416
  agents_parser = subparsers.add_parser("agents", aliases=["a"], help="list, search or inspect available agents")
346
417
  agents_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
347
- agents_parser.add_argument("action", nargs="?", default="list", choices=["list", "search", "show"])
418
+ agents_parser.add_argument("action", nargs="?", default="list", choices=["list", "search", "show", "create", "validate", "local-list"])
348
419
  agents_parser.add_argument("query", nargs="?")
420
+ agents_parser.add_argument("--description")
421
+ agents_parser.add_argument("--force", action="store_true")
349
422
 
350
423
  capabilities_parser = subparsers.add_parser(
351
424
  "capabilities",
@@ -360,9 +433,34 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
360
433
 
361
434
  local_parser = subparsers.add_parser("local", help="manage local Agent DevKit extensions")
362
435
  local_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
363
- local_parser.add_argument("action", nargs="?", default="list", choices=["list", "add", "disable", "enable", "remove", "validate"])
436
+ local_parser.add_argument("action", nargs="?", default="list", choices=["list", "add", "disable", "enable", "remove", "validate", "automation", "agents", "agent"])
364
437
  local_parser.add_argument("extension_id", nargs="?")
438
+ local_parser.add_argument("local_item_id", nargs="?")
365
439
  local_parser.add_argument("--path")
440
+ local_parser.add_argument("--title")
441
+ local_parser.add_argument("--prompt")
442
+ local_parser.add_argument("--command", dest="local_command")
443
+ local_parser.add_argument("--every")
444
+ local_parser.add_argument("--cron")
445
+ local_parser.add_argument("--force", action="store_true")
446
+ local_parser.add_argument("--yes", action="store_true")
447
+
448
+ skill_parser = subparsers.add_parser("skill", help="create and manage local Agent DevKit skills")
449
+ skill_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
450
+ skill_parser.add_argument("action", nargs="?", default="list", choices=["create", "list", "show", "update", "delete"])
451
+ skill_parser.add_argument("skill_id", nargs="?")
452
+ skill_parser.add_argument("--description")
453
+ skill_parser.add_argument("--force", action="store_true")
454
+ skill_parser.add_argument("--yes", action="store_true")
455
+
456
+ script_parser = subparsers.add_parser("script", help="create and run local scripts")
457
+ script_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
458
+ script_parser.add_argument("action", nargs="?", default="list", choices=["create", "list", "run"])
459
+ script_parser.add_argument("script_id", nargs="?")
460
+ script_parser.add_argument("--command", dest="script_command")
461
+ script_parser.add_argument("--force", action="store_true")
462
+ script_parser.add_argument("--dry-run", action="store_true")
463
+ script_parser.add_argument("--yes", action="store_true")
366
464
 
367
465
  workflow_parser = subparsers.add_parser("workflow", help="list, install or run installable workflows")
368
466
  workflow_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
@@ -371,6 +469,44 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
371
469
  workflow_parser.add_argument("--dry-run", action="store_true")
372
470
  workflow_parser.add_argument("--yes", action="store_true")
373
471
 
472
+ team_parser = subparsers.add_parser("team", help="manage project-local team profiles")
473
+ team_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
474
+ team_parser.add_argument("action", nargs="?", default="status", choices=["init", "status", "doctor", "onboard", "profile"])
475
+ team_parser.add_argument("profile_action", nargs="?", choices=["list", "show", "use", "export", "import"])
476
+ team_parser.add_argument("profile_id", nargs="?")
477
+ team_parser.add_argument("--path", dest="profile_path")
478
+ team_parser.add_argument("--force", action="store_true")
479
+
480
+ knowledge_parser = subparsers.add_parser("knowledge", help="manage file-first shared knowledge bases")
481
+ knowledge_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
482
+ knowledge_parser.add_argument(
483
+ "action",
484
+ nargs="?",
485
+ default="doctor",
486
+ choices=["init", "doctor", "search", "index", "reindex", "snapshot", "review", "curate", "publish", "sync"],
487
+ )
488
+ knowledge_parser.add_argument("target", nargs="?")
489
+ knowledge_parser.add_argument("snapshot_action", nargs="?")
490
+ knowledge_parser.add_argument("--title")
491
+ knowledge_parser.add_argument("--content")
492
+ knowledge_parser.add_argument("--from-file")
493
+ knowledge_parser.add_argument("--type", dest="entry_type")
494
+ knowledge_parser.add_argument("--owner-agent")
495
+ knowledge_parser.add_argument("--force", action="store_true")
496
+ knowledge_parser.add_argument("--yes", action="store_true")
497
+
498
+ knowledge_base_parser = subparsers.add_parser("knowledge-base", help="manage file-first shared knowledge bases")
499
+ knowledge_base_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
500
+ knowledge_base_parser.add_argument(
501
+ "action",
502
+ nargs="?",
503
+ default="status",
504
+ choices=["create", "join", "status", "tokens", "rotate-token"],
505
+ )
506
+ knowledge_base_parser.add_argument("target", nargs="?")
507
+ knowledge_base_parser.add_argument("--provider", default="local")
508
+ knowledge_base_parser.add_argument("--force", action="store_true")
509
+
374
510
  for contribution_command in ("contribute", "contribution"):
375
511
  contribution_parser = subparsers.add_parser(contribution_command, help="prepare or review local extension contributions")
376
512
  contribution_parser.add_argument("--json", action="store_true", default=argparse.SUPPRESS, help=argparse.SUPPRESS)
@@ -378,9 +514,11 @@ def build_parser(prog: str | None = None) -> argparse.ArgumentParser:
378
514
  "action",
379
515
  nargs="?",
380
516
  default="list" if contribution_command == "contribute" else "checklist",
381
- choices=["list", "prepare", "validate", "review", "checklist"],
517
+ choices=["list", "prepare", "validate", "review", "checklist", "pr"],
382
518
  )
383
519
  contribution_parser.add_argument("extension_id", nargs="?")
520
+ contribution_parser.add_argument("--dry-run", action="store_true")
521
+ contribution_parser.add_argument("--yes", action="store_true")
384
522
 
385
523
  inspect_parser = subparsers.add_parser(
386
524
  "inspect",
@@ -2,17 +2,21 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import re
6
7
  from pathlib import Path
7
8
  from typing import Any
8
9
 
9
10
  from cli.aikit.extensions import load_extensions
11
+ from cli.aikit.memory import sanitize_segment
10
12
 
11
13
 
12
14
  CONTRIBUTION_SCHEMA_VERSION = "agent-devkit.contribution/v1"
13
15
  SECRET_PATTERN = re.compile(r"(token|secret|password|api[_-]?key|pat)\s*[:=]", re.IGNORECASE)
14
16
  PRIVATE_URL_PATTERN = re.compile(r"https?://(?:localhost|127\.0\.0\.1|10\.|192\.168\.|172\.(?:1[6-9]|2\d|3[0-1])\.)", re.IGNORECASE)
15
17
  LOCAL_PATH_PATTERN = re.compile(r"(/Users/|/home/|C:\\\\Users\\\\)")
18
+ PII_PATTERN = re.compile(r"\b\d{3}\.\d{3}\.\d{3}-\d{2}\b|\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b")
19
+ CORPORATE_TERMS_ENV = "AGENT_DEVKIT_CORPORATE_TERMS"
16
20
 
17
21
 
18
22
  def contribution_list() -> dict[str, Any]:
@@ -21,7 +25,15 @@ def contribution_list() -> dict[str, Any]:
21
25
  "kind": "contributions",
22
26
  "schema_version": CONTRIBUTION_SCHEMA_VERSION,
23
27
  "status": "ok",
24
- "items": [{"id": item.get("id"), "path": item.get("path"), "enabled": item.get("enabled") is True} for item in extensions],
28
+ "items": [
29
+ {
30
+ "id": item.get("id"),
31
+ "path": sanitize_path_for_report(item.get("path")),
32
+ "path_sanitized": True,
33
+ "enabled": item.get("enabled") is True,
34
+ }
35
+ for item in extensions
36
+ ],
25
37
  }
26
38
 
27
39
 
@@ -32,6 +44,7 @@ def contribution_checklist(extension_id: str) -> dict[str, Any]:
32
44
  "kind": "contribution-checklist",
33
45
  "schema_version": CONTRIBUTION_SCHEMA_VERSION,
34
46
  "status": "blocked",
47
+ "exit_code": 2,
35
48
  "extension_id": extension_id,
36
49
  "checks": [{"id": "extension-exists", "status": "failed"}],
37
50
  }
@@ -40,6 +53,7 @@ def contribution_checklist(extension_id: str) -> dict[str, Any]:
40
53
  "kind": "contribution-checklist",
41
54
  "schema_version": CONTRIBUTION_SCHEMA_VERSION,
42
55
  "status": "passed" if all(check["status"] == "passed" for check in checks) else "blocked",
56
+ "exit_code": 0 if all(check["status"] == "passed" for check in checks) else 2,
43
57
  "extension_id": extension_id,
44
58
  "checks": checks,
45
59
  "requires_human_confirmation_for_pr": True,
@@ -54,22 +68,79 @@ def contribution_validate(extension_id: str) -> dict[str, Any]:
54
68
 
55
69
  def contribution_prepare(extension_id: str) -> dict[str, Any]:
56
70
  checklist = contribution_checklist(extension_id)
71
+ status = "planned" if checklist["status"] == "passed" else "blocked"
57
72
  return {
58
73
  "kind": "contribution-prepare",
59
74
  "schema_version": CONTRIBUTION_SCHEMA_VERSION,
60
- "status": "planned" if checklist["status"] == "passed" else "blocked",
75
+ "status": status,
76
+ "exit_code": 0 if status == "planned" else 2,
61
77
  "extension_id": extension_id,
62
78
  "checklist": checklist,
63
79
  "creates_pr": False,
64
80
  }
65
81
 
66
82
 
83
+ def contribution_pr(extension_id: str, *, dry_run: bool = True, yes: bool = False) -> dict[str, Any]:
84
+ checklist = contribution_checklist(extension_id)
85
+ safe_id = sanitize_segment(extension_id)
86
+ plan = {
87
+ "external_writes": True,
88
+ "requires_confirmation": True,
89
+ "mode": "dry-run" if dry_run or not yes else "apply",
90
+ "commands": [
91
+ f"git checkout -b feat/contribution-{safe_id}",
92
+ "git add <sanitized-extension-files>",
93
+ f"git commit -m 'Add Agent DevKit contribution {safe_id}'",
94
+ "gh pr create --fill",
95
+ ],
96
+ "stores_secret": False,
97
+ "local_paths_sanitized": True,
98
+ }
99
+ if checklist["status"] != "passed":
100
+ return {
101
+ "kind": "contribution-pr",
102
+ "schema_version": CONTRIBUTION_SCHEMA_VERSION,
103
+ "status": "blocked",
104
+ "exit_code": 2,
105
+ "extension_id": extension_id,
106
+ "checklist": checklist,
107
+ "plan": plan,
108
+ "reason": "contribution_checklist_failed",
109
+ }
110
+ if dry_run or not yes:
111
+ return {
112
+ "kind": "contribution-pr",
113
+ "schema_version": CONTRIBUTION_SCHEMA_VERSION,
114
+ "status": "planned",
115
+ "exit_code": 0,
116
+ "extension_id": extension_id,
117
+ "checklist": checklist,
118
+ "plan": plan,
119
+ "message": "PR contribution plan generated in report-only mode.",
120
+ }
121
+ return {
122
+ "kind": "contribution-pr",
123
+ "schema_version": CONTRIBUTION_SCHEMA_VERSION,
124
+ "status": "blocked",
125
+ "exit_code": 2,
126
+ "extension_id": extension_id,
127
+ "checklist": checklist,
128
+ "plan": plan,
129
+ "reason": "external_write_confirmation_required",
130
+ "next_steps": [
131
+ "Review the generated plan and run the git/gh commands manually or through a future confirmed writer.",
132
+ "Agent DevKit does not open contribution PRs automatically from this runtime path.",
133
+ ],
134
+ }
135
+
136
+
67
137
  def contribution_review(extension_id: str) -> dict[str, Any]:
68
138
  checklist = contribution_checklist(extension_id)
69
139
  return {
70
140
  "kind": "contribution-review",
71
141
  "schema_version": CONTRIBUTION_SCHEMA_VERSION,
72
142
  "status": checklist["status"],
143
+ "exit_code": 0 if checklist["status"] == "passed" else 2,
73
144
  "extension_id": extension_id,
74
145
  "findings": [check for check in checklist["checks"] if check["status"] != "passed"],
75
146
  }
@@ -84,11 +155,24 @@ def find_extension(extension_id: str) -> dict[str, Any] | None:
84
155
 
85
156
  def contribution_checks(path: Path) -> list[dict[str, Any]]:
86
157
  text = read_extension_text(path)
158
+ corporate_terms = configured_corporate_terms()
159
+ structure = extension_structure_kind(path)
87
160
  return [
88
161
  {"id": "extension-exists", "status": "passed" if path.exists() else "failed"},
162
+ {
163
+ "id": "recognized-extension-structure",
164
+ "status": "passed" if structure else "failed",
165
+ "type": structure or "unknown",
166
+ },
89
167
  {"id": "no-secret-literals", "status": "failed" if SECRET_PATTERN.search(text) else "passed"},
90
168
  {"id": "no-private-url", "status": "failed" if PRIVATE_URL_PATTERN.search(text) else "passed"},
91
169
  {"id": "no-local-path", "status": "failed" if LOCAL_PATH_PATTERN.search(text) else "passed"},
170
+ {"id": "no-pii", "status": "failed" if PII_PATTERN.search(text) else "passed"},
171
+ {
172
+ "id": "no-corporate-name",
173
+ "status": "failed" if contains_configured_corporate_term(text, corporate_terms) else "passed",
174
+ "terms_configured": len(corporate_terms),
175
+ },
92
176
  {"id": "human-pr-confirmation", "status": "passed"},
93
177
  ]
94
178
 
@@ -103,3 +187,49 @@ def read_extension_text(path: Path) -> str:
103
187
  if child.is_file() and child.suffix in {".md", ".yaml", ".yml", ".json", ".py", ".sh", ".txt"}:
104
188
  chunks.append(child.read_text(encoding="utf-8", errors="ignore")[:50_000])
105
189
  return "\n".join(chunks)[:300_000]
190
+
191
+
192
+ def extension_structure_kind(path: Path) -> str | None:
193
+ if not path.exists():
194
+ return None
195
+ if path.is_file():
196
+ if path.suffix in {".py", ".sh"}:
197
+ return "script"
198
+ if path.suffix in {".md", ".yaml", ".yml", ".json"}:
199
+ return "file"
200
+ return None
201
+ if (path / "agent.yaml").exists():
202
+ return "agent"
203
+ if (path / "SKILL.md").exists() or (path / "skill.md").exists():
204
+ return "skill"
205
+ if any((path / name).exists() for name in ("workflow.yaml", "workflow.yml")):
206
+ return "workflow"
207
+ if any(path.rglob("agent.yaml")):
208
+ return "agent"
209
+ if any(path.rglob("SKILL.md")) or any(path.rglob("skill.md")):
210
+ return "skill"
211
+ if any(path.rglob("workflow.yaml")) or any(path.rglob("workflow.yml")):
212
+ return "workflow"
213
+ if any(child.is_file() and child.suffix in {".py", ".sh"} for child in path.rglob("*")):
214
+ return "script"
215
+ return None
216
+
217
+
218
+ def sanitize_path_for_report(value: Any) -> str | None:
219
+ if not value:
220
+ return None
221
+ name = Path(str(value)).name
222
+ return f"<local-extension-path>/{sanitize_segment(name)}"
223
+
224
+
225
+ def configured_corporate_terms() -> list[str]:
226
+ raw = os.environ.get(CORPORATE_TERMS_ENV, "")
227
+ terms = [term.strip().lower() for term in re.split(r"[,;\n]+", raw) if len(term.strip()) >= 3]
228
+ return sorted(set(terms))
229
+
230
+
231
+ def contains_configured_corporate_term(text: str, terms: list[str]) -> bool:
232
+ if not terms:
233
+ return False
234
+ lowered = text.lower()
235
+ return any(term in lowered for term in terms)
@@ -7,9 +7,18 @@ from pathlib import Path
7
7
  from typing import Any
8
8
 
9
9
  from cli.aikit.capability_runtime import list_agents, list_all_capabilities
10
+ from cli.aikit.catalog import catalog_list
10
11
  from cli.aikit.diagnostics import build_diagnostics
12
+ from cli.aikit.eval import eval_list
13
+ from cli.aikit.extensions import local_extensions_list
14
+ from cli.aikit.knowledge_base import knowledge_doctor
11
15
  from cli.aikit.lock import lock_status
16
+ from cli.aikit.local_llm import local_llm_doctor
12
17
  from cli.aikit.runtime_paths import AGENTS_DIR, ROOT
18
+ from cli.aikit.secrets import secrets_doctor
19
+ from cli.aikit.specialist_readiness import specialist_readiness
20
+ from cli.aikit.team import team_status
21
+ from cli.aikit.workflows import workflow_list
13
22
 
14
23
 
15
24
  def doctor(project: str | None = None, home: str | None = None, scope: str = "auto") -> dict[str, Any]:
@@ -50,6 +59,7 @@ def doctor(project: str | None = None, home: str | None = None, scope: str = "au
50
59
  runtime_status="ok" if not errors else "error",
51
60
  locks=locks,
52
61
  )
62
+ operational = operational_diagnostics(project_path)
53
63
 
54
64
  return {
55
65
  "kind": "doctor",
@@ -68,6 +78,7 @@ def doctor(project: str | None = None, home: str | None = None, scope: str = "au
68
78
  "warnings": warnings,
69
79
  "locks": locks,
70
80
  "diagnostics": diagnostics,
81
+ "operational": operational,
71
82
  }
72
83
 
73
84
 
@@ -77,3 +88,77 @@ def doctor_project_path(project: str | None, scope: str) -> Path | None:
77
88
  if scope == "project":
78
89
  return Path.cwd()
79
90
  return None
91
+
92
+
93
+ def operational_diagnostics(project_path: Path | None = None) -> dict[str, Any]:
94
+ return {
95
+ "catalog": summarize_call(lambda: catalog_list(ROOT), count_key="count"),
96
+ "evals": summarize_call(lambda: eval_list(), count_path=("suites",)),
97
+ "workflows": summarize_call(lambda: workflow_list(ROOT), count_path=("items",)),
98
+ "secret_refs": summarize_secret_refs(),
99
+ "local_llm": summarize_local_llm(),
100
+ "specialists": summarize_specialists(),
101
+ "extensions": summarize_call(lambda: local_extensions_list(), count_path=("items",)),
102
+ "team": summarize_call(lambda: team_status(project_path or Path.cwd())),
103
+ "knowledge": summarize_call(lambda: knowledge_doctor(project_path or Path.cwd())),
104
+ }
105
+
106
+
107
+ def summarize_call(factory, *, count_key: str | None = None, count_path: tuple[str, ...] | None = None) -> dict[str, Any]:
108
+ try:
109
+ payload = factory()
110
+ except Exception as exc: # noqa: BLE001 - doctor must report subsystem failures.
111
+ return {"status": "error", "error": type(exc).__name__}
112
+ count = payload.get(count_key) if count_key else None
113
+ if count is None and count_path:
114
+ value: Any = payload
115
+ for key in count_path:
116
+ value = value.get(key) if isinstance(value, dict) else None
117
+ count = len(value) if isinstance(value, list) else None
118
+ return {"status": payload.get("status", "ok"), "kind": payload.get("kind"), "count": count}
119
+
120
+
121
+ def summarize_secret_refs() -> dict[str, Any]:
122
+ try:
123
+ payload = secrets_doctor()
124
+ except Exception as exc: # noqa: BLE001
125
+ return {"status": "error", "error": type(exc).__name__}
126
+ return {
127
+ "status": payload.get("status"),
128
+ "kind": payload.get("kind"),
129
+ "references": len(payload.get("references") or []),
130
+ "stored_values": payload.get("stored_values") is True,
131
+ }
132
+
133
+
134
+ def summarize_local_llm() -> dict[str, Any]:
135
+ try:
136
+ payload = local_llm_doctor()
137
+ except Exception as exc: # noqa: BLE001
138
+ return {"status": "error", "error": type(exc).__name__}
139
+ mini = payload.get("mini_brain") if isinstance(payload.get("mini_brain"), dict) else {}
140
+ ollama = payload.get("ollama") if isinstance(payload.get("ollama"), dict) else {}
141
+ return {
142
+ "status": payload.get("status"),
143
+ "kind": payload.get("kind"),
144
+ "ollama": ollama.get("status"),
145
+ "mini_brain": mini.get("status"),
146
+ "enabled": mini.get("enabled") is True,
147
+ }
148
+
149
+
150
+ def summarize_specialists() -> dict[str, Any]:
151
+ try:
152
+ payload = specialist_readiness(ROOT)
153
+ except Exception as exc: # noqa: BLE001
154
+ return {"status": "error", "error": type(exc).__name__}
155
+ return {
156
+ "status": payload.get("status"),
157
+ "kind": payload.get("kind"),
158
+ "agents_total": payload.get("agents_total"),
159
+ "agents_with_provider_requirements": payload.get("agents_with_provider_requirements"),
160
+ "ready_agents": payload.get("ready_agents"),
161
+ "partial_agents": payload.get("partial_agents"),
162
+ "needs_setup_agents": payload.get("needs_setup_agents"),
163
+ "missing_providers": payload.get("missing_providers") or [],
164
+ }