mana-agent 0.0.16__py3-none-any.whl

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 (390) hide show
  1. mana_agent/__init__.py +6 -0
  2. mana_agent/_version.py +46 -0
  3. mana_agent/agent/__init__.py +13 -0
  4. mana_agent/agent/evaluation_gate.py +174 -0
  5. mana_agent/agent/evidence_queue.py +72 -0
  6. mana_agent/agent/flow.py +109 -0
  7. mana_agent/agent/orchestrator.py +212 -0
  8. mana_agent/agent/selection.py +56 -0
  9. mana_agent/agent/task_classifier.py +172 -0
  10. mana_agent/agent/task_context.py +70 -0
  11. mana_agent/agent/verification.py +36 -0
  12. mana_agent/agent/verification_planner.py +150 -0
  13. mana_agent/analysis/__init__.py +0 -0
  14. mana_agent/analysis/checks.py +156 -0
  15. mana_agent/analysis/chunker.py +92 -0
  16. mana_agent/analysis/models.py +369 -0
  17. mana_agent/api/__init__.py +2 -0
  18. mana_agent/api/app.py +86 -0
  19. mana_agent/api/exceptions.py +11 -0
  20. mana_agent/api/routes/__init__.py +2 -0
  21. mana_agent/api/routes/analyze.py +46 -0
  22. mana_agent/api/routes/conversations.py +189 -0
  23. mana_agent/api/routes/events_ws.py +140 -0
  24. mana_agent/api/routes/repository_analyze.py +226 -0
  25. mana_agent/api/routes/workspaces.py +212 -0
  26. mana_agent/api/services/__init__.py +2 -0
  27. mana_agent/api/services/analyze_service.py +200 -0
  28. mana_agent/api/services/job_service.py +58 -0
  29. mana_agent/api/services/zip_service.py +80 -0
  30. mana_agent/automations/__init__.py +60 -0
  31. mana_agent/automations/github_integration.py +23 -0
  32. mana_agent/automations/scheduler.py +28 -0
  33. mana_agent/automations/self_improvement.py +134 -0
  34. mana_agent/automations/service.py +392 -0
  35. mana_agent/builtin_skills/__init__.py +2 -0
  36. mana_agent/builtin_skills/skill_creator/SKILL.md +54 -0
  37. mana_agent/builtin_skills/skill_creator/__init__.py +35 -0
  38. mana_agent/builtin_skills/skill_creator/completion.py +50 -0
  39. mana_agent/builtin_skills/skill_creator/evaluator.py +106 -0
  40. mana_agent/builtin_skills/skill_creator/generator.py +271 -0
  41. mana_agent/builtin_skills/skill_creator/renderer.py +101 -0
  42. mana_agent/builtin_skills/skill_creator/schema.py +268 -0
  43. mana_agent/builtin_skills/skill_creator/session_loader.py +75 -0
  44. mana_agent/builtin_skills/skill_creator/storage.py +260 -0
  45. mana_agent/builtin_skills/skill_creator/validator.py +132 -0
  46. mana_agent/chat/__init__.py +28 -0
  47. mana_agent/chat/events.py +150 -0
  48. mana_agent/chat/history.py +173 -0
  49. mana_agent/cli/__init__.py +5 -0
  50. mana_agent/cli/chat_ui.py +557 -0
  51. mana_agent/cli/events.py +456 -0
  52. mana_agent/cli/menu.py +5 -0
  53. mana_agent/cli/renderers.py +771 -0
  54. mana_agent/coding/__init__.py +23 -0
  55. mana_agent/coding/backend.py +26 -0
  56. mana_agent/coding/models.py +121 -0
  57. mana_agent/coding/orchestrator.py +32 -0
  58. mana_agent/coding/registry.py +45 -0
  59. mana_agent/coding/routing_policy.py +22 -0
  60. mana_agent/commands/__init__.py +1 -0
  61. mana_agent/commands/analyze_formats.py +174 -0
  62. mana_agent/commands/browser_cli.py +19 -0
  63. mana_agent/commands/chat_analyze_command.py +572 -0
  64. mana_agent/commands/chat_cli.py +3629 -0
  65. mana_agent/commands/chat_input.py +140 -0
  66. mana_agent/commands/cli.py +54 -0
  67. mana_agent/commands/cli_internal.py +1741 -0
  68. mana_agent/commands/codex_cli.py +66 -0
  69. mana_agent/commands/email_cli.py +68 -0
  70. mana_agent/commands/main_cli.py +133 -0
  71. mana_agent/commands/output.py +153 -0
  72. mana_agent/commands/telegram_cli.py +170 -0
  73. mana_agent/commands/ui_helpers.py +2610 -0
  74. mana_agent/commands/workspace_cli.py +204 -0
  75. mana_agent/commands/worktree_cli.py +221 -0
  76. mana_agent/config/__init__.py +3 -0
  77. mana_agent/config/settings.py +167 -0
  78. mana_agent/config/skills.py +39 -0
  79. mana_agent/config/user_config.py +500 -0
  80. mana_agent/connectors/__init__.py +0 -0
  81. mana_agent/connectors/browser/__init__.py +7 -0
  82. mana_agent/connectors/browser/approval.py +36 -0
  83. mana_agent/connectors/browser/contracts.py +35 -0
  84. mana_agent/connectors/browser/models.py +43 -0
  85. mana_agent/connectors/browser/runtime_tools.py +37 -0
  86. mana_agent/connectors/browser/session.py +305 -0
  87. mana_agent/connectors/email/__init__.py +4 -0
  88. mana_agent/connectors/email/approval.py +23 -0
  89. mana_agent/connectors/email/auth/__init__.py +2 -0
  90. mana_agent/connectors/email/auth/credential_store.py +32 -0
  91. mana_agent/connectors/email/auth/oauth.py +35 -0
  92. mana_agent/connectors/email/config.py +19 -0
  93. mana_agent/connectors/email/exceptions.py +80 -0
  94. mana_agent/connectors/email/models.py +216 -0
  95. mana_agent/connectors/email/permissions.py +6 -0
  96. mana_agent/connectors/email/providers/__init__.py +3 -0
  97. mana_agent/connectors/email/providers/base.py +43 -0
  98. mana_agent/connectors/email/providers/gmail.py +193 -0
  99. mana_agent/connectors/email/registry.py +15 -0
  100. mana_agent/connectors/email/runtime_tools.py +175 -0
  101. mana_agent/connectors/email/sanitizer.py +40 -0
  102. mana_agent/connectors/email/tools/__init__.py +2 -0
  103. mana_agent/connectors/email/tools/contracts.py +9 -0
  104. mana_agent/connectors/telegram/__init__.py +6 -0
  105. mana_agent/connectors/telegram/access.py +47 -0
  106. mana_agent/connectors/telegram/admin.py +119 -0
  107. mana_agent/connectors/telegram/attachments.py +59 -0
  108. mana_agent/connectors/telegram/chat.py +168 -0
  109. mana_agent/connectors/telegram/client.py +110 -0
  110. mana_agent/connectors/telegram/commands.py +77 -0
  111. mana_agent/connectors/telegram/config.py +155 -0
  112. mana_agent/connectors/telegram/connector.py +131 -0
  113. mana_agent/connectors/telegram/errors.py +33 -0
  114. mana_agent/connectors/telegram/models.py +59 -0
  115. mana_agent/connectors/telegram/normalizer.py +42 -0
  116. mana_agent/connectors/telegram/observability.py +16 -0
  117. mana_agent/connectors/telegram/pipeline.py +83 -0
  118. mana_agent/connectors/telegram/queue.py +75 -0
  119. mana_agent/connectors/telegram/renderer.py +54 -0
  120. mana_agent/connectors/telegram/store.py +224 -0
  121. mana_agent/connectors/telegram/transports/__init__.py +4 -0
  122. mana_agent/connectors/telegram/transports/polling.py +123 -0
  123. mana_agent/connectors/telegram/transports/webhook.py +64 -0
  124. mana_agent/dashboard/__init__.py +1 -0
  125. mana_agent/dashboard/app.py +79 -0
  126. mana_agent/dashboard/components/__init__.py +1 -0
  127. mana_agent/dashboard/components/chat_timeline.py +190 -0
  128. mana_agent/dashboard/pages/__init__.py +1 -0
  129. mana_agent/dashboard/pages/analyze.py +174 -0
  130. mana_agent/dashboard/pages/automations.py +73 -0
  131. mana_agent/dashboard/pages/chat.py +186 -0
  132. mana_agent/dashboard/pages/cron.py +52 -0
  133. mana_agent/dashboard/pages/metrics.py +23 -0
  134. mana_agent/dashboard/pages/observability.py +42 -0
  135. mana_agent/dashboard/pages/overview.py +44 -0
  136. mana_agent/dashboard/pages/reports.py +25 -0
  137. mana_agent/dashboard/pages/skills.py +91 -0
  138. mana_agent/dashboard/pages/taskboard.py +42 -0
  139. mana_agent/default_skills/__init__.py +1 -0
  140. mana_agent/default_skills/celery.md +18 -0
  141. mana_agent/default_skills/cli.md +24 -0
  142. mana_agent/default_skills/coding-agent.md +22 -0
  143. mana_agent/default_skills/django.md +18 -0
  144. mana_agent/default_skills/fastapi.md +17 -0
  145. mana_agent/default_skills/git.md +23 -0
  146. mana_agent/default_skills/laravel.md +17 -0
  147. mana_agent/default_skills/nestjs.md +17 -0
  148. mana_agent/default_skills/nextjs.md +17 -0
  149. mana_agent/default_skills/planning.md +22 -0
  150. mana_agent/default_skills/reactjs.md +17 -0
  151. mana_agent/default_skills/security.md +50 -0
  152. mana_agent/default_skills/telegram-bot.md +18 -0
  153. mana_agent/default_skills/testing.md +22 -0
  154. mana_agent/default_skills/vue.md +18 -0
  155. mana_agent/dependencies/__init__.py +3 -0
  156. mana_agent/dependencies/dependency_service.py +309 -0
  157. mana_agent/describe/describe_service.py +304 -0
  158. mana_agent/documents/__init__.py +7 -0
  159. mana_agent/documents/cache.py +53 -0
  160. mana_agent/documents/detector.py +40 -0
  161. mana_agent/documents/query.py +50 -0
  162. mana_agent/documents/readers.py +258 -0
  163. mana_agent/documents/service.py +548 -0
  164. mana_agent/documents/types.py +75 -0
  165. mana_agent/documents/writers.py +1085 -0
  166. mana_agent/gateway/__init__.py +30 -0
  167. mana_agent/gateway/chat_gateway.py +448 -0
  168. mana_agent/gateway/config.py +124 -0
  169. mana_agent/gateway/stack.py +458 -0
  170. mana_agent/gateway/turn_engine.py +874 -0
  171. mana_agent/integrations/__init__.py +1 -0
  172. mana_agent/integrations/codex/__init__.py +14 -0
  173. mana_agent/integrations/codex/backend.py +268 -0
  174. mana_agent/integrations/codex/client.py +210 -0
  175. mana_agent/integrations/codex/coding_agent_shim.py +298 -0
  176. mana_agent/integrations/codex/config.py +43 -0
  177. mana_agent/integrations/codex/event_adapter.py +70 -0
  178. mana_agent/integrations/codex/exceptions.py +20 -0
  179. mana_agent/integrations/codex/health.py +83 -0
  180. mana_agent/integrations/codex/prompt_builder.py +59 -0
  181. mana_agent/integrations/codex/result_parser.py +103 -0
  182. mana_agent/mcp/__init__.py +19 -0
  183. mana_agent/mcp/client.py +138 -0
  184. mana_agent/mcp/config.py +183 -0
  185. mana_agent/mcp/server.py +95 -0
  186. mana_agent/mcp/tools.py +65 -0
  187. mana_agent/models.py +284 -0
  188. mana_agent/multi_agent/__init__.py +15 -0
  189. mana_agent/multi_agent/agents/base_agent.py +143 -0
  190. mana_agent/multi_agent/agents/coding_agent.py +59 -0
  191. mana_agent/multi_agent/agents/head_decision_agent.py +55 -0
  192. mana_agent/multi_agent/agents/main_agent.py +888 -0
  193. mana_agent/multi_agent/agents/planner_agent.py +36 -0
  194. mana_agent/multi_agent/agents/research_agent.py +6 -0
  195. mana_agent/multi_agent/agents/reviewer_agent.py +102 -0
  196. mana_agent/multi_agent/agents/summarizer_agent.py +20 -0
  197. mana_agent/multi_agent/agents/tool_agent.py +15 -0
  198. mana_agent/multi_agent/agents/verifier_agent.py +154 -0
  199. mana_agent/multi_agent/codex_pool.py +94 -0
  200. mana_agent/multi_agent/communication/decision_room.py +111 -0
  201. mana_agent/multi_agent/communication/discussion.py +65 -0
  202. mana_agent/multi_agent/communication/mailbox.py +5 -0
  203. mana_agent/multi_agent/communication/message_bus.py +96 -0
  204. mana_agent/multi_agent/communication/protocols.py +9 -0
  205. mana_agent/multi_agent/core/errors.py +14 -0
  206. mana_agent/multi_agent/core/events.py +3 -0
  207. mana_agent/multi_agent/core/ids.py +54 -0
  208. mana_agent/multi_agent/core/state.py +9 -0
  209. mana_agent/multi_agent/core/types.py +558 -0
  210. mana_agent/multi_agent/events.py +3 -0
  211. mana_agent/multi_agent/memory/__init__.py +27 -0
  212. mana_agent/multi_agent/memory/agent_memory.py +13 -0
  213. mana_agent/multi_agent/memory/memory_bundle.py +143 -0
  214. mana_agent/multi_agent/memory/repo_context.py +9 -0
  215. mana_agent/multi_agent/memory/service.py +35 -0
  216. mana_agent/multi_agent/memory/task_memory.py +5 -0
  217. mana_agent/multi_agent/observability/logger.py +5 -0
  218. mana_agent/multi_agent/observability/metrics.py +12 -0
  219. mana_agent/multi_agent/observability/trace.py +64 -0
  220. mana_agent/multi_agent/queue/locks.py +15 -0
  221. mana_agent/multi_agent/queue/queue_manager.py +401 -0
  222. mana_agent/multi_agent/queue/queue_models.py +3 -0
  223. mana_agent/multi_agent/queue/scheduler.py +10 -0
  224. mana_agent/multi_agent/registry/agent_registry.py +129 -0
  225. mana_agent/multi_agent/registry/capability_registry.py +16 -0
  226. mana_agent/multi_agent/registry/route_registry.py +9 -0
  227. mana_agent/multi_agent/routing/agent_decision.py +455 -0
  228. mana_agent/multi_agent/routing/handoff.py +3 -0
  229. mana_agent/multi_agent/routing/hierarchy.py +234 -0
  230. mana_agent/multi_agent/routing/policies.py +24 -0
  231. mana_agent/multi_agent/routing/router.py +50 -0
  232. mana_agent/multi_agent/runtime/__init__.py +0 -0
  233. mana_agent/multi_agent/runtime/agent_session.py +92 -0
  234. mana_agent/multi_agent/runtime/agent_work_queue.py +2767 -0
  235. mana_agent/multi_agent/runtime/agent_work_queue_adapters.py +782 -0
  236. mana_agent/multi_agent/runtime/ask_agent.py +2601 -0
  237. mana_agent/multi_agent/runtime/auto_chat.py +303 -0
  238. mana_agent/multi_agent/runtime/automation_hooks.py +71 -0
  239. mana_agent/multi_agent/runtime/coding_agent.py +2484 -0
  240. mana_agent/multi_agent/runtime/coding_agent_models.py +114 -0
  241. mana_agent/multi_agent/runtime/coding_agent_prompt.py +223 -0
  242. mana_agent/multi_agent/runtime/compatibility.py +220 -0
  243. mana_agent/multi_agent/runtime/delegation.py +42 -0
  244. mana_agent/multi_agent/runtime/edit_scope.py +134 -0
  245. mana_agent/multi_agent/runtime/entry_router.py +251 -0
  246. mana_agent/multi_agent/runtime/evidence_ledger.py +179 -0
  247. mana_agent/multi_agent/runtime/evidence_memory.py +3 -0
  248. mana_agent/multi_agent/runtime/execution_scope.py +313 -0
  249. mana_agent/multi_agent/runtime/gate_command.py +542 -0
  250. mana_agent/multi_agent/runtime/goal_profiles.py +167 -0
  251. mana_agent/multi_agent/runtime/model_levels.py +68 -0
  252. mana_agent/multi_agent/runtime/mutation_plan.py +602 -0
  253. mana_agent/multi_agent/runtime/prompts.py +859 -0
  254. mana_agent/multi_agent/runtime/qna_chain.py +50 -0
  255. mana_agent/multi_agent/runtime/redis_tool_tasks.py +101 -0
  256. mana_agent/multi_agent/runtime/repo_chain.py +311 -0
  257. mana_agent/multi_agent/runtime/route_executor.py +562 -0
  258. mana_agent/multi_agent/runtime/run_logger.py +61 -0
  259. mana_agent/multi_agent/runtime/small_direct_edit.py +219 -0
  260. mana_agent/multi_agent/runtime/tool_worker_process.py +2046 -0
  261. mana_agent/multi_agent/runtime/tools_executor.py +448 -0
  262. mana_agent/multi_agent/runtime/tools_manager.py +2717 -0
  263. mana_agent/multi_agent/taskboard/models.py +3 -0
  264. mana_agent/multi_agent/taskboard/selectors.py +11 -0
  265. mana_agent/multi_agent/taskboard/store.py +143 -0
  266. mana_agent/multi_agent/taskboard/taskboard.py +328 -0
  267. mana_agent/multi_agent/taskboard/validators.py +33 -0
  268. mana_agent/multi_agent/tools/git_tools.py +622 -0
  269. mana_agent/multi_agent/tools/permissions.py +58 -0
  270. mana_agent/multi_agent/tools/tool_manager.py +397 -0
  271. mana_agent/multi_agent/tools/tool_request.py +3 -0
  272. mana_agent/multi_agent/tools/tool_result.py +3 -0
  273. mana_agent/multi_agent/worktrees/__init__.py +20 -0
  274. mana_agent/multi_agent/worktrees/events.py +76 -0
  275. mana_agent/multi_agent/worktrees/manager.py +742 -0
  276. mana_agent/multi_agent/worktrees/models.py +278 -0
  277. mana_agent/multi_agent/worktrees/review.py +98 -0
  278. mana_agent/multi_agent/worktrees/store.py +114 -0
  279. mana_agent/observability/__init__.py +5 -0
  280. mana_agent/observability/service.py +265 -0
  281. mana_agent/parsers/__init__.py +0 -0
  282. mana_agent/parsers/multi_parser.py +131 -0
  283. mana_agent/parsers/python_parser.py +106 -0
  284. mana_agent/prompting/__init__.py +2 -0
  285. mana_agent/prompting/builder.py +353 -0
  286. mana_agent/prompting/layers.py +56 -0
  287. mana_agent/prompting/memory_snapshot.py +39 -0
  288. mana_agent/prompting/mode_rules.py +18 -0
  289. mana_agent/prompting/output_contract.py +42 -0
  290. mana_agent/prompting/repo_rules.py +35 -0
  291. mana_agent/prompting/skills_index.py +56 -0
  292. mana_agent/renderers/__init__.py +3 -0
  293. mana_agent/renderers/html_report.py +613 -0
  294. mana_agent/search/__init__.py +20 -0
  295. mana_agent/search/config.py +85 -0
  296. mana_agent/search/decision.py +265 -0
  297. mana_agent/search/github_provider.py +164 -0
  298. mana_agent/search/memory.py +178 -0
  299. mana_agent/search/models.py +110 -0
  300. mana_agent/search/prompts.py +35 -0
  301. mana_agent/search/providers.py +33 -0
  302. mana_agent/search/ranker.py +49 -0
  303. mana_agent/search/router.py +166 -0
  304. mana_agent/search/web_provider.py +175 -0
  305. mana_agent/services/__init__.py +0 -0
  306. mana_agent/services/ask_service.py +394 -0
  307. mana_agent/services/chat_service.py +200 -0
  308. mana_agent/services/coding_memory_service.py +1106 -0
  309. mana_agent/services/coding_todo_service.py +139 -0
  310. mana_agent/services/conversation_service.py +373 -0
  311. mana_agent/services/dependency_service.py +3 -0
  312. mana_agent/services/describe_service.py +3 -0
  313. mana_agent/services/execution_event_hub.py +294 -0
  314. mana_agent/services/index_service.py +238 -0
  315. mana_agent/services/memory_service.py +831 -0
  316. mana_agent/services/parsers/__init__.py +19 -0
  317. mana_agent/services/parsers/base.py +18 -0
  318. mana_agent/services/parsers/dart_parser.py +37 -0
  319. mana_agent/services/parsers/js_ts_parser.py +46 -0
  320. mana_agent/services/parsers/jvm_parser.py +32 -0
  321. mana_agent/services/parsers/markup_parser.py +14 -0
  322. mana_agent/services/parsers/native_parser.py +135 -0
  323. mana_agent/services/parsers/python_parser.py +105 -0
  324. mana_agent/services/parsers/scripting_parser.py +27 -0
  325. mana_agent/services/project_analyze_service.py +1514 -0
  326. mana_agent/services/project_llm_analyze_service.py +426 -0
  327. mana_agent/services/report_service.py +734 -0
  328. mana_agent/services/search_service.py +244 -0
  329. mana_agent/services/structure_service.py +468 -0
  330. mana_agent/services/vulnerability_service.py +199 -0
  331. mana_agent/skills/__init__.py +21 -0
  332. mana_agent/skills/adaptive.py +362 -0
  333. mana_agent/skills/chat.py +177 -0
  334. mana_agent/skills/manager.py +473 -0
  335. mana_agent/telemetry/session_trace.py +44 -0
  336. mana_agent/telemetry/tokens.py +257 -0
  337. mana_agent/tools/__init__.py +88 -0
  338. mana_agent/tools/apply_patch.py +1413 -0
  339. mana_agent/tools/catalog.py +497 -0
  340. mana_agent/tools/contracts.py +721 -0
  341. mana_agent/tools/edit_file.py +236 -0
  342. mana_agent/tools/repository.py +665 -0
  343. mana_agent/tools/write_file.py +605 -0
  344. mana_agent/tui/__init__.py +23 -0
  345. mana_agent/tui/app.py +1158 -0
  346. mana_agent/tui/app.tcss +131 -0
  347. mana_agent/tui/banner.py +9 -0
  348. mana_agent/tui/forms.py +31 -0
  349. mana_agent/tui/menu.py +61 -0
  350. mana_agent/tui/model_picker.py +160 -0
  351. mana_agent/tui/search_provider_picker.py +78 -0
  352. mana_agent/tui/status.py +26 -0
  353. mana_agent/tui/widgets/__init__.py +6 -0
  354. mana_agent/tui/widgets/chat_log.py +360 -0
  355. mana_agent/tui/widgets/tool_card.py +236 -0
  356. mana_agent/tui/wizard.py +263 -0
  357. mana_agent/ui/__init__.py +7 -0
  358. mana_agent/ui/banner.py +56 -0
  359. mana_agent/ui/streamlit_helpers.py +616 -0
  360. mana_agent/utils/__init__.py +0 -0
  361. mana_agent/utils/guards.py +32 -0
  362. mana_agent/utils/index_discovery.py +51 -0
  363. mana_agent/utils/io.py +225 -0
  364. mana_agent/utils/logging.py +35 -0
  365. mana_agent/utils/project_discovery.py +137 -0
  366. mana_agent/utils/project_search.py +239 -0
  367. mana_agent/utils/redaction.py +96 -0
  368. mana_agent/utils/tool_policy.py +147 -0
  369. mana_agent/utils/tool_results.py +45 -0
  370. mana_agent/utils/tools_run.py +55 -0
  371. mana_agent/vector_store/__init__.py +0 -0
  372. mana_agent/vector_store/embeddings.py +88 -0
  373. mana_agent/vector_store/faiss_store.py +172 -0
  374. mana_agent/workspaces/__init__.py +25 -0
  375. mana_agent/workspaces/context.py +40 -0
  376. mana_agent/workspaces/discovery.py +47 -0
  377. mana_agent/workspaces/impact.py +73 -0
  378. mana_agent/workspaces/models.py +184 -0
  379. mana_agent/workspaces/paths.py +85 -0
  380. mana_agent/workspaces/relationships.py +124 -0
  381. mana_agent/workspaces/routing.py +101 -0
  382. mana_agent/workspaces/search.py +76 -0
  383. mana_agent/workspaces/service.py +351 -0
  384. mana_agent/workspaces/store.py +95 -0
  385. mana_agent-0.0.16.dist-info/LICENSE +21 -0
  386. mana_agent-0.0.16.dist-info/METADATA +1051 -0
  387. mana_agent-0.0.16.dist-info/RECORD +390 -0
  388. mana_agent-0.0.16.dist-info/WHEEL +5 -0
  389. mana_agent-0.0.16.dist-info/entry_points.txt +2 -0
  390. mana_agent-0.0.16.dist-info/top_level.txt +1 -0
mana_agent/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """mana_agent package."""
2
+
3
+ from mana_agent._version import get_version
4
+
5
+ __all__ = ["__version__"]
6
+ __version__ = get_version()
mana_agent/_version.py ADDED
@@ -0,0 +1,46 @@
1
+ """Single-source package version resolution from pyproject.toml."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from functools import lru_cache
6
+ from pathlib import Path
7
+
8
+ _DIST_NAME = "mana-agent"
9
+
10
+
11
+ def _read_pyproject_version(start: Path) -> str | None:
12
+ """Walk parents from *start* and return ``[project].version`` if present."""
13
+ for directory in (start, *start.parents):
14
+ path = directory / "pyproject.toml"
15
+ if not path.is_file():
16
+ continue
17
+ try:
18
+ import tomllib
19
+
20
+ data = tomllib.loads(path.read_text(encoding="utf-8"))
21
+ version = data.get("project", {}).get("version")
22
+ if isinstance(version, str) and version.strip():
23
+ return version.strip()
24
+ except Exception:
25
+ continue
26
+ return None
27
+
28
+
29
+ @lru_cache(maxsize=1)
30
+ def get_version() -> str:
31
+ """Return the package version.
32
+
33
+ Preference order:
34
+ 1. ``[project].version`` from the nearest ``pyproject.toml`` (source tree).
35
+ 2. Installed distribution metadata for ``mana-agent``.
36
+ 3. ``\"dev\"`` when neither is available.
37
+ """
38
+ from_pyproject = _read_pyproject_version(Path(__file__).resolve().parent)
39
+ if from_pyproject:
40
+ return from_pyproject
41
+ try:
42
+ from importlib.metadata import version
43
+
44
+ return version(_DIST_NAME)
45
+ except Exception:
46
+ return "dev"
@@ -0,0 +1,13 @@
1
+ """Agent flow primitives shared by ManaAgent orchestration paths."""
2
+
3
+ from mana_agent.agent.orchestrator import AgentOrchestrator
4
+ from mana_agent.agent.task_classifier import TaskDecision, classify_task
5
+ from mana_agent.agent.verification_planner import VerificationDecision, plan_verification
6
+
7
+ __all__ = [
8
+ "AgentOrchestrator",
9
+ "TaskDecision",
10
+ "VerificationDecision",
11
+ "classify_task",
12
+ "plan_verification",
13
+ ]
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from enum import Enum
5
+ from pathlib import Path
6
+ from typing import Any, Literal
7
+
8
+ from mana_agent.agent.evidence_queue import EvidenceQueue
9
+ from mana_agent.agent.task_classifier import TaskDecision
10
+
11
+
12
+ class AgentRunPhase(str, Enum):
13
+ RECEIVED = "RECEIVED"
14
+ CLASSIFIED = "CLASSIFIED"
15
+ PLANNED = "PLANNED"
16
+ GATHERING_EVIDENCE = "GATHERING_EVIDENCE"
17
+ READY_TO_MUTATE = "READY_TO_MUTATE"
18
+ MUTATING = "MUTATING"
19
+ VERIFYING = "VERIFYING"
20
+ READY_TO_FINALIZE = "READY_TO_FINALIZE"
21
+ DONE = "DONE"
22
+ FAILED_RECOVERABLE = "FAILED_RECOVERABLE"
23
+ FAILED_FINAL = "FAILED_FINAL"
24
+
25
+
26
+ ExecutionDecision = Literal[
27
+ "execute_next_tool",
28
+ "skip_tool",
29
+ "stop_discovery",
30
+ "start_mutation",
31
+ "run_verification",
32
+ "ask_user",
33
+ "finalize",
34
+ "abort_due_to_error",
35
+ ]
36
+
37
+
38
+ @dataclass(slots=True)
39
+ class AgentRunState:
40
+ phase: AgentRunPhase = AgentRunPhase.RECEIVED
41
+ files_read: set[str] = field(default_factory=set)
42
+ changed_files: set[str] = field(default_factory=set)
43
+ failed_tool_calls: list[str] = field(default_factory=list)
44
+ warnings: list[str] = field(default_factory=list)
45
+ verification_status: str = ""
46
+ loop_counters: dict[str, int] = field(default_factory=dict)
47
+ evidence_sufficient: bool = False
48
+ mutation_succeeded: bool = False
49
+
50
+
51
+ @dataclass(frozen=True, slots=True)
52
+ class GateResult:
53
+ decision: ExecutionDecision
54
+ reason: str
55
+ next_action: str = ""
56
+ confidence: float = 0.8
57
+ warnings: tuple[str, ...] = ()
58
+
59
+ def trace_row(self, *, changed_files: list[str] | None = None) -> dict[str, Any]:
60
+ return {
61
+ "layer": "evaluation_gate",
62
+ "decision": self.decision,
63
+ "reason": self.reason,
64
+ "next_action": self.next_action,
65
+ "confidence": self.confidence,
66
+ "changed_files": list(changed_files or []),
67
+ "warnings": list(self.warnings),
68
+ }
69
+
70
+
71
+ class EvaluationGate:
72
+ def __init__(self, *, repo_root: str | Path, max_repeated_tool: int = 1, max_repeated_error: int = 1) -> None:
73
+ self.repo_root = Path(repo_root).resolve()
74
+ self.max_repeated_tool = max(1, int(max_repeated_tool))
75
+ self.max_repeated_error = max(1, int(max_repeated_error))
76
+
77
+ def target_section_found(self, decision: TaskDecision, *, path: str) -> bool:
78
+ if not decision.target_sections:
79
+ return True
80
+ target = self.repo_root / path
81
+ try:
82
+ text = target.read_text(encoding="utf-8", errors="replace")
83
+ except OSError:
84
+ return False
85
+ headings = {
86
+ " ".join(line.lstrip("#").strip().split()).lower()
87
+ for line in text.splitlines()
88
+ if line.lstrip().startswith("#")
89
+ }
90
+ return any(section.lower() in headings for section in decision.target_sections)
91
+
92
+ def evidence_is_sufficient(
93
+ self,
94
+ *,
95
+ decision: TaskDecision,
96
+ state: AgentRunState,
97
+ ) -> bool:
98
+ if decision.scope == "single_file_section" and decision.target_files:
99
+ target = decision.target_files[0]
100
+ return target in state.files_read and self.target_section_found(decision, path=target)
101
+ if decision.scope == "single_file" and decision.target_files:
102
+ return decision.target_files[0] in state.files_read
103
+ if decision.scope == "multi_file" and decision.target_files:
104
+ return set(decision.target_files).issubset(state.files_read)
105
+ return False
106
+
107
+ def after_tool(
108
+ self,
109
+ *,
110
+ decision: TaskDecision,
111
+ state: AgentRunState,
112
+ evidence_queue: EvidenceQueue,
113
+ tool_name: str,
114
+ ok: bool,
115
+ files_read: list[str],
116
+ changed_files: list[str],
117
+ error: str = "",
118
+ ) -> GateResult:
119
+ key = f"{tool_name}:{','.join(files_read or changed_files)}"
120
+ state.loop_counters[key] = state.loop_counters.get(key, 0) + 1
121
+ state.files_read.update(files_read)
122
+ state.changed_files.update(changed_files)
123
+ if error:
124
+ state.failed_tool_calls.append(error)
125
+ if not ok and error and state.failed_tool_calls.count(error) > self.max_repeated_error:
126
+ state.phase = AgentRunPhase.FAILED_RECOVERABLE
127
+ return GateResult("abort_due_to_error", f"repeated tool error stopped: {error}", confidence=0.9)
128
+ if state.loop_counters[key] > self.max_repeated_tool and tool_name:
129
+ return GateResult("skip_tool", f"repeated tool call stopped: {tool_name}", confidence=0.9)
130
+ for path in files_read:
131
+ evidence_queue.mark_done("read", path, evidence={"source": "tool"})
132
+ state.evidence_sufficient = self.evidence_is_sufficient(decision=decision, state=state)
133
+ if state.evidence_sufficient and decision.needs_mutation:
134
+ skipped = evidence_queue.skip_where(
135
+ lambda item: item.action_type in {"search", "read"} and item.target not in decision.target_files,
136
+ reason="target evidence is sufficient",
137
+ )
138
+ state.phase = AgentRunPhase.READY_TO_MUTATE
139
+ return GateResult(
140
+ "start_mutation",
141
+ "target file was read and requested section exists; unrelated discovery is optional",
142
+ next_action="apply_patch",
143
+ confidence=0.91,
144
+ warnings=(f"skipped_pending_discovery:{skipped}",) if skipped else (),
145
+ )
146
+ if changed_files and decision.needs_verification:
147
+ state.phase = AgentRunPhase.VERIFYING
148
+ return GateResult("run_verification", "mutation changed files; verification is now possible")
149
+ if changed_files:
150
+ state.phase = AgentRunPhase.READY_TO_FINALIZE
151
+ return GateResult("finalize", "mutation changed files and no verification was required")
152
+ return GateResult("execute_next_tool", "continue with the next approved tool")
153
+
154
+ def before_tool(
155
+ self,
156
+ *,
157
+ decision: TaskDecision,
158
+ state: AgentRunState,
159
+ tool_name: str,
160
+ target: str = "",
161
+ ) -> GateResult:
162
+ if state.evidence_sufficient and tool_name in {"repo_search", "repo_batch_search", "semantic_search", "list_files"}:
163
+ return GateResult("skip_tool", "evidence is already sufficient; broad discovery is skipped")
164
+ if (
165
+ state.evidence_sufficient
166
+ and tool_name == "read_file"
167
+ and decision.target_files
168
+ and target not in set(decision.target_files)
169
+ ):
170
+ return GateResult("skip_tool", "target evidence is already sufficient; unrelated read is skipped")
171
+ return GateResult("execute_next_tool", "tool call is necessary for the current state")
172
+
173
+
174
+ __all__ = ["AgentRunPhase", "AgentRunState", "EvaluationGate", "ExecutionDecision", "GateResult"]
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Callable, Literal
5
+
6
+
7
+ EvidenceAction = Literal["search", "read", "patch", "verify", "summarize"]
8
+ EvidenceStatus = Literal["pending", "running", "done", "skipped", "failed"]
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class EvidenceQueueItem:
13
+ action_type: EvidenceAction
14
+ target: str
15
+ reason: str
16
+ priority: int = 50
17
+ required: bool = False
18
+ status: EvidenceStatus = "pending"
19
+ evidence: dict[str, Any] = field(default_factory=dict)
20
+ changed_files: list[str] = field(default_factory=list)
21
+ retry_count: int = 0
22
+ skip_reason: str = ""
23
+
24
+
25
+ class EvidenceQueue:
26
+ def __init__(self, items: list[EvidenceQueueItem] | None = None) -> None:
27
+ self._items = list(items or [])
28
+
29
+ def add(self, item: EvidenceQueueItem) -> None:
30
+ self._items.append(item)
31
+
32
+ def items(self) -> list[EvidenceQueueItem]:
33
+ return list(self._items)
34
+
35
+ def pending(self) -> list[EvidenceQueueItem]:
36
+ return [item for item in self._items if item.status == "pending"]
37
+
38
+ def mark_done(self, action_type: str, target: str, *, evidence: dict[str, Any] | None = None) -> None:
39
+ for item in self._items:
40
+ if item.action_type == action_type and item.target == target and item.status in {"pending", "running"}:
41
+ item.status = "done"
42
+ item.evidence.update(evidence or {})
43
+ return
44
+
45
+ def skip_where(self, predicate: Callable[[EvidenceQueueItem], bool], *, reason: str) -> int:
46
+ skipped = 0
47
+ for item in self._items:
48
+ if item.status == "pending" and predicate(item):
49
+ item.status = "skipped"
50
+ item.skip_reason = reason
51
+ skipped += 1
52
+ return skipped
53
+
54
+ def trace_row(self) -> dict[str, Any]:
55
+ return {
56
+ "layer": "evidence_queue",
57
+ "decision": "queue_state",
58
+ "reason": "evidence queue snapshot",
59
+ "items": [
60
+ {
61
+ "action_type": item.action_type,
62
+ "target": item.target,
63
+ "required": item.required,
64
+ "status": item.status,
65
+ "skip_reason": item.skip_reason,
66
+ }
67
+ for item in self._items
68
+ ],
69
+ }
70
+
71
+
72
+ __all__ = ["EvidenceAction", "EvidenceQueue", "EvidenceQueueItem", "EvidenceStatus"]
@@ -0,0 +1,109 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from mana_agent.agent.selection import AgentPhase, select_task
8
+ from mana_agent.agent.task_context import TaskContext
9
+ from mana_agent.agent.verification import VerificationPlan, default_verification_plan
10
+
11
+
12
+ FLOW_ORDER: tuple[AgentPhase, ...] = (
13
+ AgentPhase.DISCOVER,
14
+ AgentPhase.SELECT,
15
+ AgentPhase.READ,
16
+ AgentPhase.ACT,
17
+ AgentPhase.VERIFY,
18
+ AgentPhase.SUMMARIZE,
19
+ )
20
+
21
+
22
+ @dataclass(frozen=True, slots=True)
23
+ class AgentFlow:
24
+ context: TaskContext
25
+ verification: VerificationPlan
26
+
27
+
28
+ def _short_goal(request: str) -> str:
29
+ cleaned = " ".join(str(request or "").split())
30
+ if not cleaned:
31
+ return "Handle the current user request."
32
+ return cleaned[:180]
33
+
34
+
35
+ def _candidate_search_terms(request: str, candidate_files: tuple[str, ...]) -> tuple[str, ...]:
36
+ terms: list[str] = []
37
+ terms.extend(candidate_files[:3])
38
+ for match in re.findall(r"`([^`]{2,120})`", request):
39
+ terms.append(match.strip())
40
+ for match in re.findall(r"\b[A-Za-z_][A-Za-z0-9_]{2,}\b", request):
41
+ if match.lower() not in {"the", "and", "for", "with", "this", "that", "from", "into", "when"}:
42
+ terms.append(match)
43
+ if len(terms) >= 8:
44
+ break
45
+ seen: set[str] = set()
46
+ unique: list[str] = []
47
+ for term in terms:
48
+ if term and term not in seen:
49
+ seen.add(term)
50
+ unique.append(term)
51
+ return tuple(unique[:8])
52
+
53
+
54
+ def _done_criteria(mode: str) -> tuple[str, ...]:
55
+ if mode == "plan_only":
56
+ return (
57
+ "plan includes goal, requirements, implementation steps, verification, and done criteria",
58
+ "no repository files are edited",
59
+ )
60
+ if mode == "analyze":
61
+ return (
62
+ "analysis is backed by repository evidence",
63
+ "risks and recommended next steps are explicit",
64
+ )
65
+ if mode == "verify":
66
+ return (
67
+ "relevant checks are run or a concrete blocker is reported",
68
+ "final response includes exact command outcomes",
69
+ )
70
+ if mode == "edit":
71
+ return (
72
+ "requested code changes are implemented",
73
+ "changed files are reviewed",
74
+ "relevant verification is run or a blocker is reported",
75
+ )
76
+ return ("the user receives a direct, useful, honest answer",)
77
+
78
+
79
+ def build_agent_flow(
80
+ request: str,
81
+ *,
82
+ repo_root: str | Path | None = None,
83
+ explicit_mode: str | None = None,
84
+ candidate_files: tuple[str, ...] = (),
85
+ files_read: tuple[str, ...] = (),
86
+ flow_context: str | None = None,
87
+ ) -> AgentFlow:
88
+ selection = select_task(
89
+ request,
90
+ explicit_mode=explicit_mode,
91
+ candidate_files=candidate_files,
92
+ files_read=files_read,
93
+ )
94
+ verification = default_verification_plan(mode=selection.mode.value, request=request)
95
+ context = TaskContext(
96
+ request=request,
97
+ mode=selection.mode.value,
98
+ phase=selection.phase,
99
+ goal=_short_goal(request),
100
+ explicit_requirements=(request.strip(),) if request.strip() else (),
101
+ repo_root=Path(repo_root).resolve() if repo_root is not None else None,
102
+ candidate_files=selection.candidate_files,
103
+ files_read=tuple(files_read),
104
+ candidate_search_terms=_candidate_search_terms(request, selection.candidate_files),
105
+ done_criteria=_done_criteria(selection.mode.value),
106
+ verification_plan=verification.commands or verification.notes,
107
+ flow_context=flow_context,
108
+ )
109
+ return AgentFlow(context=context, verification=verification)
@@ -0,0 +1,212 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+ from typing import Any, Sequence
6
+
7
+ from mana_agent.agent.evaluation_gate import AgentRunPhase, AgentRunState, EvaluationGate, GateResult
8
+ from mana_agent.agent.evidence_queue import EvidenceQueue, EvidenceQueueItem
9
+ from mana_agent.agent.task_classifier import TaskDecision, classify_task
10
+ from mana_agent.agent.verification_planner import VerificationDecision, plan_verification
11
+ from mana_agent.multi_agent.runtime.execution_scope import ExecutionScopeDecision, ScopeLevel
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class TaskPlan:
16
+ decision: TaskDecision
17
+ evidence_queue: EvidenceQueue
18
+ trace: tuple[dict[str, Any], ...] = ()
19
+
20
+
21
+ class PlanBuilder:
22
+ def build(self, decision: TaskDecision) -> TaskPlan:
23
+ queue = EvidenceQueue()
24
+ if decision.needs_file_read:
25
+ for path in decision.target_files:
26
+ queue.add(
27
+ EvidenceQueueItem(
28
+ action_type="read",
29
+ target=path,
30
+ reason="explicit target file named by the user",
31
+ priority=10,
32
+ required=True,
33
+ )
34
+ )
35
+ if decision.needs_repo_search:
36
+ queue.add(
37
+ EvidenceQueueItem(
38
+ action_type="search",
39
+ target="repo",
40
+ reason="no explicit target file was available",
41
+ priority=20,
42
+ required=True,
43
+ )
44
+ )
45
+ if decision.needs_mutation:
46
+ queue.add(
47
+ EvidenceQueueItem(
48
+ action_type="patch",
49
+ target=",".join(decision.target_files) or "resolved target",
50
+ reason="mutation is required after sufficient evidence",
51
+ priority=80,
52
+ required=True,
53
+ )
54
+ )
55
+ if decision.needs_verification:
56
+ queue.add(
57
+ EvidenceQueueItem(
58
+ action_type="verify",
59
+ target="changed files",
60
+ reason="verify the requested mutation",
61
+ priority=90,
62
+ required=True,
63
+ )
64
+ )
65
+ trace = (
66
+ {
67
+ "layer": "plan_builder",
68
+ "decision": "minimal_plan_built",
69
+ "reason": "explicit targets are prioritized before repository-wide discovery",
70
+ "next_action": "read_file" if decision.needs_file_read else "repo_search",
71
+ "confidence": decision.confidence,
72
+ },
73
+ queue.trace_row(),
74
+ )
75
+ return TaskPlan(decision=decision, evidence_queue=queue, trace=trace)
76
+
77
+
78
+ @dataclass(slots=True)
79
+ class CriticDecision:
80
+ progressed: bool
81
+ enough_evidence: bool
82
+ should_finalize: bool
83
+ reason: str
84
+
85
+ def trace_row(self) -> dict[str, Any]:
86
+ return {
87
+ "layer": "post_tool_critic",
88
+ "decision": "finalize" if self.should_finalize else "continue",
89
+ "reason": self.reason,
90
+ "progressed": self.progressed,
91
+ "enough_evidence": self.enough_evidence,
92
+ }
93
+
94
+
95
+ class PostToolCritic:
96
+ def review(self, *, gate: GateResult, state: AgentRunState) -> CriticDecision:
97
+ should_finalize = gate.decision == "finalize" or state.phase == AgentRunPhase.DONE
98
+ progressed = gate.decision in {"start_mutation", "run_verification", "finalize", "execute_next_tool"}
99
+ reason = gate.reason
100
+ return CriticDecision(
101
+ progressed=progressed,
102
+ enough_evidence=state.evidence_sufficient,
103
+ should_finalize=should_finalize,
104
+ reason=reason,
105
+ )
106
+
107
+
108
+ @dataclass(slots=True)
109
+ class AgentOrchestrator:
110
+ repo_root: Path
111
+ decision: TaskDecision
112
+ plan: TaskPlan
113
+ state: AgentRunState = field(default_factory=AgentRunState)
114
+ gate: EvaluationGate = field(init=False)
115
+ critic: PostToolCritic = field(default_factory=PostToolCritic)
116
+ trace: list[dict[str, Any]] = field(default_factory=list)
117
+
118
+ def __post_init__(self) -> None:
119
+ self.repo_root = Path(self.repo_root).resolve()
120
+ self.gate = EvaluationGate(repo_root=self.repo_root)
121
+ self.state.phase = AgentRunPhase.PLANNED
122
+ self.trace.append(self.decision.as_trace_row())
123
+ self.trace.extend(self.plan.trace)
124
+
125
+ @classmethod
126
+ def start(
127
+ cls,
128
+ request: str,
129
+ *,
130
+ repo_root: str | Path,
131
+ target_files: Sequence[str] = (),
132
+ requires_edit: bool | None = None,
133
+ execution_scope: ExecutionScopeDecision | None = None,
134
+ ) -> "AgentOrchestrator":
135
+ root = Path(repo_root).resolve()
136
+ if execution_scope is not None:
137
+ task_type = {
138
+ "answer": "answer_only",
139
+ "inspect": "repo_inspection",
140
+ "edit": "mutation_required",
141
+ "verify": "verification_only",
142
+ "plan": "answer_only",
143
+ }[execution_scope.task_type]
144
+ scope = {
145
+ ScopeLevel.DIRECT: "single_file" if len(execution_scope.explicit_target_files) == 1 else "multi_file",
146
+ ScopeLevel.BOUNDED: "multi_file",
147
+ ScopeLevel.IMPACT: "multi_file",
148
+ ScopeLevel.BROAD: "project_wide",
149
+ }[execution_scope.scope_level]
150
+ decision = TaskDecision(
151
+ task_type=task_type, # type: ignore[arg-type]
152
+ target_files=tuple(execution_scope.explicit_target_files),
153
+ mutation_intent=str(request).strip() if execution_scope.task_type == "edit" else "",
154
+ constraints=tuple(execution_scope.out_of_bounds),
155
+ needs_repo_search=execution_scope.max_search_operations > 0,
156
+ needs_file_read=bool(execution_scope.all_evidence_files),
157
+ needs_mutation=execution_scope.task_type == "edit",
158
+ needs_verification=execution_scope.verification_strategy != "none",
159
+ scope=scope, # type: ignore[arg-type]
160
+ confidence=execution_scope.confidence,
161
+ )
162
+ else:
163
+ decision = classify_task(request, repo_root=root, target_files=target_files, requires_edit=requires_edit)
164
+ plan = PlanBuilder().build(decision)
165
+ return cls(repo_root=root, decision=decision, plan=plan)
166
+
167
+ def before_tool(self, *, tool_name: str, target: str = "") -> GateResult:
168
+ result = self.gate.before_tool(decision=self.decision, state=self.state, tool_name=tool_name, target=target)
169
+ self.trace.append(result.trace_row(changed_files=sorted(self.state.changed_files)))
170
+ return result
171
+
172
+ def after_tool(
173
+ self,
174
+ *,
175
+ tool_name: str,
176
+ ok: bool,
177
+ files_read: list[str],
178
+ changed_files: list[str],
179
+ error: str = "",
180
+ ) -> GateResult:
181
+ result = self.gate.after_tool(
182
+ decision=self.decision,
183
+ state=self.state,
184
+ evidence_queue=self.plan.evidence_queue,
185
+ tool_name=tool_name,
186
+ ok=ok,
187
+ files_read=files_read,
188
+ changed_files=changed_files,
189
+ error=error,
190
+ )
191
+ self.trace.append(result.trace_row(changed_files=sorted(self.state.changed_files)))
192
+ self.trace.append(self.critic.review(gate=result, state=self.state).trace_row())
193
+ return result
194
+
195
+ def verification_decision(self, *, changed_files: Sequence[str], core_agent_change: bool = False) -> VerificationDecision:
196
+ decision = plan_verification(changed_files=changed_files, core_agent_change=core_agent_change)
197
+ self.trace.append(decision.trace_row())
198
+ return decision
199
+
200
+ def finalize_trace(self) -> None:
201
+ self.trace.append(
202
+ {
203
+ "layer": "final_response_builder",
204
+ "decision": "finalize",
205
+ "reason": "authoritative queue state is ready for the final response",
206
+ "changed_files": sorted(self.state.changed_files),
207
+ "warnings": list(self.state.warnings),
208
+ }
209
+ )
210
+
211
+
212
+ __all__ = ["AgentOrchestrator", "AgentRunState", "PlanBuilder", "PostToolCritic", "TaskPlan"]
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+
6
+ from mana_agent.multi_agent.runtime.auto_chat import AutoChatMode, classify_auto_chat_intent
7
+
8
+
9
+ class AgentPhase(str, Enum):
10
+ DISCOVER = "discover"
11
+ SELECT = "select"
12
+ READ = "read"
13
+ ACT = "act"
14
+ VERIFY = "verify"
15
+ SUMMARIZE = "summarize"
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class SelectionResult:
20
+ mode: AutoChatMode
21
+ phase: AgentPhase
22
+ candidate_files: tuple[str, ...] = ()
23
+
24
+
25
+ def select_mode(request: str, *, explicit_mode: str | None = None) -> AutoChatMode:
26
+ """Resolve the task mode using the same bounded classifier as chat."""
27
+ if explicit_mode:
28
+ normalized = str(explicit_mode).strip().lower().replace("-", "_")
29
+ for mode in AutoChatMode:
30
+ if normalized in {mode.value, mode.name.lower()}:
31
+ return mode
32
+ return classify_auto_chat_intent(request)
33
+
34
+
35
+ def select_initial_phase(*, candidate_files: tuple[str, ...] = (), files_read: tuple[str, ...] = ()) -> AgentPhase:
36
+ """Choose the prompt-visible phase from known task context."""
37
+ if files_read:
38
+ return AgentPhase.ACT
39
+ if candidate_files:
40
+ return AgentPhase.READ
41
+ return AgentPhase.DISCOVER
42
+
43
+
44
+ def select_task(
45
+ request: str,
46
+ *,
47
+ explicit_mode: str | None = None,
48
+ candidate_files: tuple[str, ...] = (),
49
+ files_read: tuple[str, ...] = (),
50
+ ) -> SelectionResult:
51
+ return SelectionResult(
52
+ mode=select_mode(request, explicit_mode=explicit_mode),
53
+ phase=select_initial_phase(candidate_files=candidate_files, files_read=files_read),
54
+ candidate_files=tuple(candidate_files),
55
+ )
56
+