minder-cli 0.5.2__tar.gz → 0.5.4__tar.gz

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 (160) hide show
  1. {minder_cli-0.5.2 → minder_cli-0.5.4}/PKG-INFO +1 -1
  2. {minder_cli-0.5.2 → minder_cli-0.5.4}/pyproject.toml +1 -1
  3. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/application/admin/dto.py +24 -0
  4. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/application/admin/use_cases.py +137 -0
  5. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/embedding/local.py +1 -3
  6. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/llm/llama_cpp_llm.py +1 -3
  7. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/api.py +99 -0
  8. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/runtime.py +24 -13
  9. {minder_cli-0.5.2 → minder_cli-0.5.4}/.gitignore +0 -0
  10. {minder_cli-0.5.2 → minder_cli-0.5.4}/LICENSE +0 -0
  11. {minder_cli-0.5.2 → minder_cli-0.5.4}/README-pypi.md +0 -0
  12. {minder_cli-0.5.2 → minder_cli-0.5.4}/README.md +0 -0
  13. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/__init__.py +0 -0
  14. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/api/routers/prompts.py +0 -0
  15. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/application/__init__.py +0 -0
  16. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/application/admin/__init__.py +0 -0
  17. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/application/admin/jobs.py +0 -0
  18. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/__init__.py +0 -0
  19. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/context.py +0 -0
  20. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/middleware.py +0 -0
  21. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/principal.py +0 -0
  22. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/rate_limiter.py +0 -0
  23. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/rbac.py +0 -0
  24. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/auth/service.py +0 -0
  25. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/bootstrap/__init__.py +0 -0
  26. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/bootstrap/agent_seeder.py +0 -0
  27. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/bootstrap/providers.py +0 -0
  28. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/bootstrap/transport.py +0 -0
  29. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/cache/__init__.py +0 -0
  30. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/cache/providers.py +0 -0
  31. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/chunking/__init__.py +0 -0
  32. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/chunking/code_splitter.py +0 -0
  33. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/chunking/splitter.py +0 -0
  34. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/cli.py +0 -0
  35. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/config.py +0 -0
  36. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/context_compactor.py +0 -0
  37. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/continuity.py +0 -0
  38. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/dev.py +0 -0
  39. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/embedding/__init__.py +0 -0
  40. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/embedding/base.py +0 -0
  41. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/embedding/openai.py +0 -0
  42. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/__init__.py +0 -0
  43. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/edges.py +0 -0
  44. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/executor.py +0 -0
  45. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/graph.py +0 -0
  46. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/__init__.py +0 -0
  47. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/clarification.py +0 -0
  48. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/evaluator.py +0 -0
  49. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/guard.py +0 -0
  50. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/llm.py +0 -0
  51. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/planning.py +0 -0
  52. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/reasoning.py +0 -0
  53. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/reflection.py +0 -0
  54. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/reranker.py +0 -0
  55. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/retriever.py +0 -0
  56. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/verification.py +0 -0
  57. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/nodes/workflow_planner.py +0 -0
  58. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/runtime.py +0 -0
  59. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/graph/state.py +0 -0
  60. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/learning/__init__.py +0 -0
  61. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/learning/error_learner.py +0 -0
  62. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/learning/pattern_extractor.py +0 -0
  63. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/learning/quality_optimizer.py +0 -0
  64. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/learning/skill_synthesizer.py +0 -0
  65. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/llm/__init__.py +0 -0
  66. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/llm/base.py +0 -0
  67. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/llm/factory.py +0 -0
  68. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/llm/openai.py +0 -0
  69. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/__init__.py +0 -0
  70. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/agent.py +0 -0
  71. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/base.py +0 -0
  72. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/client.py +0 -0
  73. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/document.py +0 -0
  74. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/error.py +0 -0
  75. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/graph.py +0 -0
  76. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/history.py +0 -0
  77. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/job.py +0 -0
  78. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/prompt.py +0 -0
  79. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/repository.py +0 -0
  80. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/rule.py +0 -0
  81. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/session.py +0 -0
  82. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/skill.py +0 -0
  83. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/user.py +0 -0
  84. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/models/workflow.py +0 -0
  85. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/observability/__init__.py +0 -0
  86. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/observability/audit.py +0 -0
  87. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/observability/logging.py +0 -0
  88. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/observability/metrics.py +0 -0
  89. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/observability/tracing.py +0 -0
  90. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/__init__.py +0 -0
  91. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/__init__.py +0 -0
  92. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/agent.py +0 -0
  93. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/auth.py +0 -0
  94. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/ide.py +0 -0
  95. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/mcp.py +0 -0
  96. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/sync.py +0 -0
  97. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/commands/update.py +0 -0
  98. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/main.py +0 -0
  99. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/utils/common.py +0 -0
  100. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/utils/config.py +0 -0
  101. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/utils/git.py +0 -0
  102. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/cli/utils/version.py +0 -0
  103. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/__init__.py +0 -0
  104. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/__init__.py +0 -0
  105. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/agents.py +0 -0
  106. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/context.py +0 -0
  107. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/dashboard.py +0 -0
  108. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/jobs.py +0 -0
  109. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/memories.py +0 -0
  110. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/prompts.py +0 -0
  111. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/routes.py +0 -0
  112. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/runtime.py +0 -0
  113. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/search.py +0 -0
  114. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/presentation/http/admin/skills.py +0 -0
  115. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/prompts/__init__.py +0 -0
  116. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/prompts/formatter.py +0 -0
  117. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/resources/__init__.py +0 -0
  118. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/retrieval/__init__.py +0 -0
  119. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/retrieval/hybrid.py +0 -0
  120. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/retrieval/mmr.py +0 -0
  121. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/retrieval/multi_hop.py +0 -0
  122. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/server.py +0 -0
  123. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/__init__.py +0 -0
  124. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/document.py +0 -0
  125. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/error.py +0 -0
  126. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/feedback.py +0 -0
  127. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/graph.py +0 -0
  128. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/history.py +0 -0
  129. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/interfaces.py +0 -0
  130. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/milvus/__init__.py +0 -0
  131. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/milvus/client.py +0 -0
  132. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/milvus/collections.py +0 -0
  133. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/milvus/vector_store.py +0 -0
  134. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/mongodb/__init__.py +0 -0
  135. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/mongodb/client.py +0 -0
  136. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/mongodb/graph_store.py +0 -0
  137. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/mongodb/indexes.py +0 -0
  138. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/mongodb/operational_store.py +0 -0
  139. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/relational.py +0 -0
  140. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/repo_state.py +0 -0
  141. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/rule.py +0 -0
  142. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/store/vector.py +0 -0
  143. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/__init__.py +0 -0
  144. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/agents.py +0 -0
  145. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/auth.py +0 -0
  146. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/graph.py +0 -0
  147. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/ingest.py +0 -0
  148. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/memory.py +0 -0
  149. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/query.py +0 -0
  150. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/registry.py +0 -0
  151. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/repo_scanner.py +0 -0
  152. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/seeds/__init__.py +0 -0
  153. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/seeds/default_agents.py +0 -0
  154. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/session.py +0 -0
  155. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/skills.py +0 -0
  156. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/tools/workflow.py +0 -0
  157. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/transport/__init__.py +0 -0
  158. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/transport/base.py +0 -0
  159. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/transport/sse.py +0 -0
  160. {minder_cli-0.5.2 → minder_cli-0.5.4}/src/minder/transport/stdio.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minder-cli
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: Minder CLI is the command-line interface for the Minder self-hosted MCP platform.
5
5
  Project-URL: Homepage, https://github.com/hiimtrung/minder
6
6
  Project-URL: Repository, https://github.com/hiimtrung/minder
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "minder-cli"
7
- version = "0.5.2"
7
+ version = "0.5.4"
8
8
  description = "Minder CLI is the command-line interface for the Minder self-hosted MCP platform."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -479,3 +479,27 @@ class RepositoryLandscapePayload(TypedDict):
479
479
  nodes: list[RepositoryLandscapeNodePayload]
480
480
  edges: list[RepositoryLandscapeEdgePayload]
481
481
  summary: dict[str, int]
482
+
483
+
484
+
485
+ class AgentPayload(TypedDict):
486
+ id: str
487
+ name: str
488
+ title: str
489
+ description: str
490
+ system_prompt: str
491
+ tools: list[str]
492
+ workflow_steps: list[str]
493
+ artifact_types: list[str]
494
+ tags: list[str]
495
+ is_default: bool
496
+ created_at: str | None
497
+ updated_at: str | None
498
+
499
+
500
+ class AgentListPayload(TypedDict):
501
+ agents: list[AgentPayload]
502
+
503
+
504
+ class AgentDetailPayload(TypedDict):
505
+ agent: AgentPayload
@@ -13,6 +13,9 @@ from minder.application.admin.dto import (
13
13
  ActivityEventPayload,
14
14
  AdminLoginPayload,
15
15
  AdminSessionPayload,
16
+ AgentDetailPayload,
17
+ AgentListPayload,
18
+ AgentPayload,
16
19
  AuditEventPayload,
17
20
  AuditListPayload,
18
21
  ClientConnectionTestPayload,
@@ -651,6 +654,116 @@ class AdminConsoleUseCases:
651
654
  ),
652
655
  }
653
656
 
657
+ # ------------------------------------------------------------------
658
+ # SubAgent management
659
+ # ------------------------------------------------------------------
660
+
661
+ async def list_agents(self) -> AgentListPayload:
662
+ agents = await self._store.list_agents()
663
+ return {"agents": [self.serialize_agent(a) for a in agents]}
664
+
665
+ async def get_agent_detail(self, agent_id: uuid.UUID) -> AgentDetailPayload:
666
+ agent = await self._store.get_agent_by_id(agent_id)
667
+ if agent is None:
668
+ raise LookupError(f"Agent {agent_id} not found")
669
+ return {"agent": self.serialize_agent(agent)}
670
+
671
+ async def create_agent(
672
+ self,
673
+ *,
674
+ name: str,
675
+ title: str = "",
676
+ description: str = "",
677
+ system_prompt: str = "",
678
+ tools: list[str] | None = None,
679
+ workflow_steps: list[str] | None = None,
680
+ artifact_types: list[str] | None = None,
681
+ tags: list[str] | None = None,
682
+ is_default: bool = False,
683
+ ) -> AgentDetailPayload:
684
+ agent = await self._store.create_agent(
685
+ name=name,
686
+ title=title,
687
+ description=description,
688
+ system_prompt=system_prompt,
689
+ tools=tools or [],
690
+ workflow_steps=workflow_steps or [],
691
+ artifact_types=artifact_types or [],
692
+ tags=tags or [],
693
+ is_default=is_default,
694
+ )
695
+ return {"agent": self.serialize_agent(agent)}
696
+
697
+ async def update_agent(
698
+ self,
699
+ agent_id: uuid.UUID,
700
+ *,
701
+ name: str | None = None,
702
+ title: str | None = None,
703
+ description: str | None = None,
704
+ system_prompt: str | None = None,
705
+ tools: list[str] | None = None,
706
+ workflow_steps: list[str] | None = None,
707
+ artifact_types: list[str] | None = None,
708
+ tags: list[str] | None = None,
709
+ is_default: bool | None = None,
710
+ ) -> AgentDetailPayload:
711
+ kwargs: dict[str, Any] = {}
712
+ if name is not None:
713
+ kwargs["name"] = name
714
+ if title is not None:
715
+ kwargs["title"] = title
716
+ if description is not None:
717
+ kwargs["description"] = description
718
+ if system_prompt is not None:
719
+ kwargs["system_prompt"] = system_prompt
720
+ if tools is not None:
721
+ kwargs["tools"] = tools
722
+ if workflow_steps is not None:
723
+ kwargs["workflow_steps"] = workflow_steps
724
+ if artifact_types is not None:
725
+ kwargs["artifact_types"] = artifact_types
726
+ if tags is not None:
727
+ kwargs["tags"] = tags
728
+ if is_default is not None:
729
+ kwargs["is_default"] = is_default
730
+ updated = await self._store.update_agent(agent_id, **kwargs)
731
+ if updated is None:
732
+ raise LookupError(f"Agent {agent_id} not found")
733
+ return {"agent": self.serialize_agent(updated)}
734
+
735
+ async def delete_agent(self, agent_id: uuid.UUID) -> dict[str, bool]:
736
+ existing = await self._store.get_agent_by_id(agent_id)
737
+ if existing is None:
738
+ raise LookupError(f"Agent {agent_id} not found")
739
+ await self._store.delete_agent(agent_id)
740
+ return {"deleted": True}
741
+
742
+ @staticmethod
743
+ def serialize_agent(agent: Any) -> AgentPayload:
744
+ return {
745
+ "id": str(agent.id),
746
+ "name": getattr(agent, "name", ""),
747
+ "title": getattr(agent, "title", ""),
748
+ "description": getattr(agent, "description", ""),
749
+ "system_prompt": getattr(agent, "system_prompt", ""),
750
+ "tools": list(getattr(agent, "tools", []) or []),
751
+ "workflow_steps": list(getattr(agent, "workflow_steps", []) or []),
752
+ "artifact_types": list(getattr(agent, "artifact_types", []) or []),
753
+ "tags": list(getattr(agent, "tags", []) or []),
754
+ "is_default": bool(getattr(agent, "is_default", False)),
755
+ "created_at": (
756
+ agent.created_at.isoformat()
757
+ if getattr(agent, "created_at", None)
758
+ else None
759
+ ),
760
+ "updated_at": (
761
+ agent.updated_at.isoformat()
762
+ if getattr(agent, "updated_at", None)
763
+ else None
764
+ ),
765
+ }
766
+
654
767
  # ------------------------------------------------------------------
655
768
  # Session management
656
769
  # ------------------------------------------------------------------
@@ -788,19 +901,43 @@ class AdminConsoleUseCases:
788
901
  if not normalized_path:
789
902
  raise ValueError("Repository path is required")
790
903
  updates["state_path"] = normalized_path
904
+ new_workflow_id: uuid.UUID | None = _UNSET # type: ignore[assignment]
791
905
  if workflow_id is not _UNSET:
792
906
  if workflow_id is None or str(workflow_id).strip() == "":
793
907
  updates["workflow_id"] = None
908
+ new_workflow_id = None
794
909
  else:
795
910
  wf_id = uuid.UUID(str(workflow_id))
796
911
  workflow = await self._store.get_workflow_by_id(wf_id)
797
912
  if workflow is None:
798
913
  raise LookupError(f"Workflow {wf_id} not found")
799
914
  updates["workflow_id"] = str(wf_id)
915
+ new_workflow_id = wf_id
800
916
 
801
917
  updated = await self._store.update_repository(repo_id, **updates)
802
918
  if updated is None:
803
919
  raise LookupError("Repository not found")
920
+
921
+ # Create workflow_state when a workflow is first assigned to a repo
922
+ if new_workflow_id is not _UNSET and new_workflow_id is not None: # type: ignore[comparison-overlap]
923
+ existing_state = await self._store.get_workflow_state_by_repo(repo_id)
924
+ if existing_state is None:
925
+ workflow_obj = await self._store.get_workflow_by_id(new_workflow_id)
926
+ steps = list(getattr(workflow_obj, "steps", []) or []) if workflow_obj else []
927
+ step_names = [
928
+ s["name"] for s in steps if isinstance(s, dict) and "name" in s
929
+ ]
930
+ first_step = step_names[0] if step_names else ""
931
+ second_step = step_names[1] if len(step_names) > 1 else None
932
+ await self._store.create_workflow_state(
933
+ repo_id=repo_id,
934
+ current_step=first_step,
935
+ completed_steps=[],
936
+ blocked_by=[],
937
+ artifacts={},
938
+ next_step=second_step,
939
+ )
940
+
804
941
  return await self.get_repository_detail(repo_id)
805
942
 
806
943
  async def delete_repository(self, repo_id: uuid.UUID) -> DeleteRepositoryPayload:
@@ -54,9 +54,7 @@ class LocalEmbeddingProvider:
54
54
  return
55
55
 
56
56
  if not llama_cpp_usable():
57
- logger.warning(
58
- "CPU does not support AVX2; llama.cpp unavailable. Using mock embedding."
59
- )
57
+ logger.warning("llama.cpp not usable on this host; embedding running in mock mode.")
60
58
  return
61
59
 
62
60
  try:
@@ -52,9 +52,7 @@ class LlamaCppLLM:
52
52
  return
53
53
 
54
54
  if not llama_cpp_usable():
55
- logger.warning(
56
- "CPU does not support AVX2; llama.cpp unavailable. Falling back to mock mode."
57
- )
55
+ logger.warning("llama.cpp not usable on this host; LLM running in mock mode.")
58
56
  return
59
57
 
60
58
  cache_key = f"{self._model_repo}:{self._model_file}"
@@ -520,6 +520,98 @@ def build_admin_api_routes(context: AdminRouteContext) -> list[BaseRoute]:
520
520
 
521
521
  return JSONResponse({"error": "Method not allowed"}, status_code=405)
522
522
 
523
+ # ------------------------------------------------------------------
524
+ # SubAgent management
525
+ # ------------------------------------------------------------------
526
+
527
+ async def admin_agents(request):
528
+ try:
529
+ await context.admin_user_from_request(request)
530
+ except PermissionError:
531
+ return JSONResponse({"error": "Admin role required"}, status_code=403)
532
+ except Exception as exc:
533
+ return JSONResponse({"error": str(exc)}, status_code=401)
534
+
535
+ if request.method == "GET":
536
+ return JSONResponse(await context.use_cases.list_agents())
537
+
538
+ if request.method == "POST":
539
+ try:
540
+ payload = await request.json()
541
+ except Exception:
542
+ return JSONResponse({"error": "Invalid JSON"}, status_code=400)
543
+ name = str(payload.get("name", "")).strip()
544
+ if not name:
545
+ return JSONResponse({"error": "name is required"}, status_code=400)
546
+ try:
547
+ return JSONResponse(
548
+ await context.use_cases.create_agent(
549
+ name=name,
550
+ title=str(payload.get("title", "")),
551
+ description=str(payload.get("description", "")),
552
+ system_prompt=str(payload.get("system_prompt", "")),
553
+ tools=payload.get("tools") or [],
554
+ workflow_steps=payload.get("workflow_steps") or [],
555
+ artifact_types=payload.get("artifact_types") or [],
556
+ tags=payload.get("tags") or [],
557
+ is_default=bool(payload.get("is_default", False)),
558
+ ),
559
+ status_code=201,
560
+ )
561
+ except Exception as exc:
562
+ return JSONResponse({"error": str(exc)}, status_code=400)
563
+
564
+ return JSONResponse({"error": "Method not allowed"}, status_code=405)
565
+
566
+ async def agent_detail(request):
567
+ try:
568
+ await context.admin_user_from_request(request)
569
+ except PermissionError:
570
+ return JSONResponse({"error": "Admin role required"}, status_code=403)
571
+ except Exception as exc:
572
+ return JSONResponse({"error": str(exc)}, status_code=401)
573
+
574
+ agent_id = uuid.UUID(str(request.path_params["agent_id"]))
575
+
576
+ if request.method == "GET":
577
+ try:
578
+ return JSONResponse(await context.use_cases.get_agent_detail(agent_id))
579
+ except LookupError:
580
+ return JSONResponse({"error": "Agent not found"}, status_code=404)
581
+
582
+ if request.method == "PATCH":
583
+ try:
584
+ payload = await request.json()
585
+ except Exception:
586
+ return JSONResponse({"error": "Invalid JSON"}, status_code=400)
587
+ try:
588
+ return JSONResponse(
589
+ await context.use_cases.update_agent(
590
+ agent_id,
591
+ name=payload.get("name"),
592
+ title=payload.get("title"),
593
+ description=payload.get("description"),
594
+ system_prompt=payload.get("system_prompt"),
595
+ tools=payload.get("tools"),
596
+ workflow_steps=payload.get("workflow_steps"),
597
+ artifact_types=payload.get("artifact_types"),
598
+ tags=payload.get("tags"),
599
+ is_default=payload.get("is_default"),
600
+ )
601
+ )
602
+ except LookupError:
603
+ return JSONResponse({"error": "Agent not found"}, status_code=404)
604
+ except Exception as exc:
605
+ return JSONResponse({"error": str(exc)}, status_code=400)
606
+
607
+ if request.method == "DELETE":
608
+ try:
609
+ return JSONResponse(await context.use_cases.delete_agent(agent_id))
610
+ except LookupError:
611
+ return JSONResponse({"error": "Agent not found"}, status_code=404)
612
+
613
+ return JSONResponse({"error": "Method not allowed"}, status_code=405)
614
+
523
615
  # ------------------------------------------------------------------
524
616
  # Workflow management
525
617
  # ------------------------------------------------------------------
@@ -1331,6 +1423,13 @@ def build_admin_api_routes(context: AdminRouteContext) -> list[BaseRoute]:
1331
1423
  user_detail,
1332
1424
  methods=["GET", "PATCH", "DELETE"],
1333
1425
  ),
1426
+ # SubAgent management
1427
+ Route("/v1/admin/agents", admin_agents, methods=["GET", "POST"]),
1428
+ Route(
1429
+ "/v1/admin/agents/{agent_id:uuid}",
1430
+ agent_detail,
1431
+ methods=["GET", "PATCH", "DELETE"],
1432
+ ),
1334
1433
  # Workflow management
1335
1434
  Route("/v1/admin/workflows", admin_workflows, methods=["GET", "POST"]),
1336
1435
  Route(
@@ -26,18 +26,22 @@ def load_attr(module_name: str, attr_name: str) -> Any | None:
26
26
  def llama_cpp_usable() -> bool:
27
27
  """Return True only if llama.cpp can actually run on this CPU.
28
28
 
29
- Some CI runners report AVX2 in /proc/cpuinfo but the hypervisor blocks
30
- the instructions, causing an unrecoverable SIGILL. We probe by running
31
- a subprocess that initialises the llama.cpp backend; if the subprocess
32
- is killed by SIGILL (returncode == -signal.SIGILL) we mark the library
33
- as unavailable and fall back to mock mode. Result is cached for the
34
- lifetime of the process.
29
+ Some CI runners / VM hypervisors block AVX2 instructions even when
30
+ /proc/cpuinfo advertises them, causing an unrecoverable SIGILL. We
31
+ probe by running a subprocess that initialises the ggml backend; if the
32
+ subprocess is killed by SIGILL (returncode == -signal.SIGILL) we mark
33
+ the library as unavailable and fall back to mock mode.
34
+
35
+ Build the Docker image with CMAKE_ARGS containing -DGGML_AVX2=OFF (and
36
+ related flags) to compile a portable binary that avoids SIGILL entirely.
37
+ Result is cached for the lifetime of the process.
35
38
  """
36
39
  global _LLAMA_CPP_PROBE
37
40
  if _LLAMA_CPP_PROBE is not None:
38
41
  return _LLAMA_CPP_PROBE
39
42
 
40
43
  if not module_available("llama_cpp"):
44
+ logger.warning("llama-cpp-python is not installed. Falling back to mock mode.")
41
45
  _LLAMA_CPP_PROBE = False
42
46
  return False
43
47
 
@@ -48,19 +52,26 @@ def llama_cpp_usable() -> bool:
48
52
  "-c",
49
53
  # Instantiating Llama triggers ggml_backend_reg_count → ggml_cpu_init.
50
54
  # /dev/null causes a model-load error (returncode 1) on healthy CPUs;
51
- # SIGILL kills the process (returncode -4) on unsupported CPUs.
55
+ # SIGILL (returncode -4) means the CPU doesn't support the compiled ISA.
52
56
  "from llama_cpp import Llama; Llama(model_path='/dev/null', verbose=False)",
53
57
  ],
54
58
  capture_output=True,
55
59
  timeout=30,
56
60
  )
57
- _LLAMA_CPP_PROBE = proc.returncode != -signal.SIGILL
61
+ if proc.returncode == -signal.SIGILL:
62
+ logger.warning(
63
+ "llama.cpp unavailable: CPU or hypervisor blocked an instruction "
64
+ "(SIGILL). Rebuild the image with CMAKE_ARGS containing "
65
+ "-DGGML_AVX2=OFF to fix this. Falling back to mock mode."
66
+ )
67
+ _LLAMA_CPP_PROBE = False
68
+ else:
69
+ _LLAMA_CPP_PROBE = True
70
+ except subprocess.TimeoutExpired:
71
+ logger.warning("llama.cpp usability probe timed out. Falling back to mock mode.")
72
+ _LLAMA_CPP_PROBE = False
58
73
  except Exception as exc:
59
- logger.warning("llama.cpp usability probe error: %s", exc)
74
+ logger.warning("llama.cpp usability probe error: %s. Falling back to mock mode.", exc)
60
75
  _LLAMA_CPP_PROBE = False
61
76
 
62
- if not _LLAMA_CPP_PROBE:
63
- logger.warning(
64
- "llama.cpp unavailable on this CPU (SIGILL probe). Falling back to mock mode."
65
- )
66
77
  return _LLAMA_CPP_PROBE
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes