gobby 0.2.5__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 (383) hide show
  1. gobby/__init__.py +3 -0
  2. gobby/adapters/__init__.py +30 -0
  3. gobby/adapters/base.py +93 -0
  4. gobby/adapters/claude_code.py +276 -0
  5. gobby/adapters/codex.py +1292 -0
  6. gobby/adapters/gemini.py +343 -0
  7. gobby/agents/__init__.py +37 -0
  8. gobby/agents/codex_session.py +120 -0
  9. gobby/agents/constants.py +112 -0
  10. gobby/agents/context.py +362 -0
  11. gobby/agents/definitions.py +133 -0
  12. gobby/agents/gemini_session.py +111 -0
  13. gobby/agents/registry.py +618 -0
  14. gobby/agents/runner.py +968 -0
  15. gobby/agents/session.py +259 -0
  16. gobby/agents/spawn.py +916 -0
  17. gobby/agents/spawners/__init__.py +77 -0
  18. gobby/agents/spawners/base.py +142 -0
  19. gobby/agents/spawners/cross_platform.py +266 -0
  20. gobby/agents/spawners/embedded.py +225 -0
  21. gobby/agents/spawners/headless.py +226 -0
  22. gobby/agents/spawners/linux.py +125 -0
  23. gobby/agents/spawners/macos.py +277 -0
  24. gobby/agents/spawners/windows.py +308 -0
  25. gobby/agents/tty_config.py +319 -0
  26. gobby/autonomous/__init__.py +32 -0
  27. gobby/autonomous/progress_tracker.py +447 -0
  28. gobby/autonomous/stop_registry.py +269 -0
  29. gobby/autonomous/stuck_detector.py +383 -0
  30. gobby/cli/__init__.py +67 -0
  31. gobby/cli/__main__.py +8 -0
  32. gobby/cli/agents.py +529 -0
  33. gobby/cli/artifacts.py +266 -0
  34. gobby/cli/daemon.py +329 -0
  35. gobby/cli/extensions.py +526 -0
  36. gobby/cli/github.py +263 -0
  37. gobby/cli/init.py +53 -0
  38. gobby/cli/install.py +614 -0
  39. gobby/cli/installers/__init__.py +37 -0
  40. gobby/cli/installers/antigravity.py +65 -0
  41. gobby/cli/installers/claude.py +363 -0
  42. gobby/cli/installers/codex.py +192 -0
  43. gobby/cli/installers/gemini.py +294 -0
  44. gobby/cli/installers/git_hooks.py +377 -0
  45. gobby/cli/installers/shared.py +737 -0
  46. gobby/cli/linear.py +250 -0
  47. gobby/cli/mcp.py +30 -0
  48. gobby/cli/mcp_proxy.py +698 -0
  49. gobby/cli/memory.py +304 -0
  50. gobby/cli/merge.py +384 -0
  51. gobby/cli/projects.py +79 -0
  52. gobby/cli/sessions.py +622 -0
  53. gobby/cli/tasks/__init__.py +30 -0
  54. gobby/cli/tasks/_utils.py +658 -0
  55. gobby/cli/tasks/ai.py +1025 -0
  56. gobby/cli/tasks/commits.py +169 -0
  57. gobby/cli/tasks/crud.py +685 -0
  58. gobby/cli/tasks/deps.py +135 -0
  59. gobby/cli/tasks/labels.py +63 -0
  60. gobby/cli/tasks/main.py +273 -0
  61. gobby/cli/tasks/search.py +178 -0
  62. gobby/cli/tui.py +34 -0
  63. gobby/cli/utils.py +513 -0
  64. gobby/cli/workflows.py +927 -0
  65. gobby/cli/worktrees.py +481 -0
  66. gobby/config/__init__.py +129 -0
  67. gobby/config/app.py +551 -0
  68. gobby/config/extensions.py +167 -0
  69. gobby/config/features.py +472 -0
  70. gobby/config/llm_providers.py +98 -0
  71. gobby/config/logging.py +66 -0
  72. gobby/config/mcp.py +346 -0
  73. gobby/config/persistence.py +247 -0
  74. gobby/config/servers.py +141 -0
  75. gobby/config/sessions.py +250 -0
  76. gobby/config/tasks.py +784 -0
  77. gobby/hooks/__init__.py +104 -0
  78. gobby/hooks/artifact_capture.py +213 -0
  79. gobby/hooks/broadcaster.py +243 -0
  80. gobby/hooks/event_handlers.py +723 -0
  81. gobby/hooks/events.py +218 -0
  82. gobby/hooks/git.py +169 -0
  83. gobby/hooks/health_monitor.py +171 -0
  84. gobby/hooks/hook_manager.py +856 -0
  85. gobby/hooks/hook_types.py +575 -0
  86. gobby/hooks/plugins.py +813 -0
  87. gobby/hooks/session_coordinator.py +396 -0
  88. gobby/hooks/verification_runner.py +268 -0
  89. gobby/hooks/webhooks.py +339 -0
  90. gobby/install/claude/commands/gobby/bug.md +51 -0
  91. gobby/install/claude/commands/gobby/chore.md +51 -0
  92. gobby/install/claude/commands/gobby/epic.md +52 -0
  93. gobby/install/claude/commands/gobby/eval.md +235 -0
  94. gobby/install/claude/commands/gobby/feat.md +49 -0
  95. gobby/install/claude/commands/gobby/nit.md +52 -0
  96. gobby/install/claude/commands/gobby/ref.md +52 -0
  97. gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
  98. gobby/install/claude/hooks/hook_dispatcher.py +364 -0
  99. gobby/install/claude/hooks/validate_settings.py +102 -0
  100. gobby/install/claude/hooks-template.json +118 -0
  101. gobby/install/codex/hooks/hook_dispatcher.py +153 -0
  102. gobby/install/codex/prompts/forget.md +7 -0
  103. gobby/install/codex/prompts/memories.md +7 -0
  104. gobby/install/codex/prompts/recall.md +7 -0
  105. gobby/install/codex/prompts/remember.md +13 -0
  106. gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
  107. gobby/install/gemini/hooks-template.json +138 -0
  108. gobby/install/shared/plugins/code_guardian.py +456 -0
  109. gobby/install/shared/plugins/example_notify.py +331 -0
  110. gobby/integrations/__init__.py +10 -0
  111. gobby/integrations/github.py +145 -0
  112. gobby/integrations/linear.py +145 -0
  113. gobby/llm/__init__.py +40 -0
  114. gobby/llm/base.py +120 -0
  115. gobby/llm/claude.py +578 -0
  116. gobby/llm/claude_executor.py +503 -0
  117. gobby/llm/codex.py +322 -0
  118. gobby/llm/codex_executor.py +513 -0
  119. gobby/llm/executor.py +316 -0
  120. gobby/llm/factory.py +34 -0
  121. gobby/llm/gemini.py +258 -0
  122. gobby/llm/gemini_executor.py +339 -0
  123. gobby/llm/litellm.py +287 -0
  124. gobby/llm/litellm_executor.py +303 -0
  125. gobby/llm/resolver.py +499 -0
  126. gobby/llm/service.py +236 -0
  127. gobby/mcp_proxy/__init__.py +29 -0
  128. gobby/mcp_proxy/actions.py +175 -0
  129. gobby/mcp_proxy/daemon_control.py +198 -0
  130. gobby/mcp_proxy/importer.py +436 -0
  131. gobby/mcp_proxy/lazy.py +325 -0
  132. gobby/mcp_proxy/manager.py +798 -0
  133. gobby/mcp_proxy/metrics.py +609 -0
  134. gobby/mcp_proxy/models.py +139 -0
  135. gobby/mcp_proxy/registries.py +215 -0
  136. gobby/mcp_proxy/schema_hash.py +381 -0
  137. gobby/mcp_proxy/semantic_search.py +706 -0
  138. gobby/mcp_proxy/server.py +549 -0
  139. gobby/mcp_proxy/services/__init__.py +0 -0
  140. gobby/mcp_proxy/services/fallback.py +306 -0
  141. gobby/mcp_proxy/services/recommendation.py +224 -0
  142. gobby/mcp_proxy/services/server_mgmt.py +214 -0
  143. gobby/mcp_proxy/services/system.py +72 -0
  144. gobby/mcp_proxy/services/tool_filter.py +231 -0
  145. gobby/mcp_proxy/services/tool_proxy.py +309 -0
  146. gobby/mcp_proxy/stdio.py +565 -0
  147. gobby/mcp_proxy/tools/__init__.py +27 -0
  148. gobby/mcp_proxy/tools/agents.py +1103 -0
  149. gobby/mcp_proxy/tools/artifacts.py +207 -0
  150. gobby/mcp_proxy/tools/hub.py +335 -0
  151. gobby/mcp_proxy/tools/internal.py +337 -0
  152. gobby/mcp_proxy/tools/memory.py +543 -0
  153. gobby/mcp_proxy/tools/merge.py +422 -0
  154. gobby/mcp_proxy/tools/metrics.py +283 -0
  155. gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
  156. gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
  157. gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
  158. gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
  159. gobby/mcp_proxy/tools/orchestration/review.py +736 -0
  160. gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
  161. gobby/mcp_proxy/tools/session_messages.py +1056 -0
  162. gobby/mcp_proxy/tools/task_dependencies.py +219 -0
  163. gobby/mcp_proxy/tools/task_expansion.py +591 -0
  164. gobby/mcp_proxy/tools/task_github.py +393 -0
  165. gobby/mcp_proxy/tools/task_linear.py +379 -0
  166. gobby/mcp_proxy/tools/task_orchestration.py +77 -0
  167. gobby/mcp_proxy/tools/task_readiness.py +522 -0
  168. gobby/mcp_proxy/tools/task_sync.py +351 -0
  169. gobby/mcp_proxy/tools/task_validation.py +843 -0
  170. gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
  171. gobby/mcp_proxy/tools/tasks/_context.py +112 -0
  172. gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
  173. gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
  174. gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
  175. gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
  176. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
  177. gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
  178. gobby/mcp_proxy/tools/tasks/_search.py +215 -0
  179. gobby/mcp_proxy/tools/tasks/_session.py +125 -0
  180. gobby/mcp_proxy/tools/workflows.py +973 -0
  181. gobby/mcp_proxy/tools/worktrees.py +1264 -0
  182. gobby/mcp_proxy/transports/__init__.py +0 -0
  183. gobby/mcp_proxy/transports/base.py +95 -0
  184. gobby/mcp_proxy/transports/factory.py +44 -0
  185. gobby/mcp_proxy/transports/http.py +139 -0
  186. gobby/mcp_proxy/transports/stdio.py +213 -0
  187. gobby/mcp_proxy/transports/websocket.py +136 -0
  188. gobby/memory/backends/__init__.py +116 -0
  189. gobby/memory/backends/mem0.py +408 -0
  190. gobby/memory/backends/memu.py +485 -0
  191. gobby/memory/backends/null.py +111 -0
  192. gobby/memory/backends/openmemory.py +537 -0
  193. gobby/memory/backends/sqlite.py +304 -0
  194. gobby/memory/context.py +87 -0
  195. gobby/memory/manager.py +1001 -0
  196. gobby/memory/protocol.py +451 -0
  197. gobby/memory/search/__init__.py +66 -0
  198. gobby/memory/search/text.py +127 -0
  199. gobby/memory/viz.py +258 -0
  200. gobby/prompts/__init__.py +13 -0
  201. gobby/prompts/defaults/expansion/system.md +119 -0
  202. gobby/prompts/defaults/expansion/user.md +48 -0
  203. gobby/prompts/defaults/external_validation/agent.md +72 -0
  204. gobby/prompts/defaults/external_validation/external.md +63 -0
  205. gobby/prompts/defaults/external_validation/spawn.md +83 -0
  206. gobby/prompts/defaults/external_validation/system.md +6 -0
  207. gobby/prompts/defaults/features/import_mcp.md +22 -0
  208. gobby/prompts/defaults/features/import_mcp_github.md +17 -0
  209. gobby/prompts/defaults/features/import_mcp_search.md +16 -0
  210. gobby/prompts/defaults/features/recommend_tools.md +32 -0
  211. gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
  212. gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
  213. gobby/prompts/defaults/features/server_description.md +20 -0
  214. gobby/prompts/defaults/features/server_description_system.md +6 -0
  215. gobby/prompts/defaults/features/task_description.md +31 -0
  216. gobby/prompts/defaults/features/task_description_system.md +6 -0
  217. gobby/prompts/defaults/features/tool_summary.md +17 -0
  218. gobby/prompts/defaults/features/tool_summary_system.md +6 -0
  219. gobby/prompts/defaults/research/step.md +58 -0
  220. gobby/prompts/defaults/validation/criteria.md +47 -0
  221. gobby/prompts/defaults/validation/validate.md +38 -0
  222. gobby/prompts/loader.py +346 -0
  223. gobby/prompts/models.py +113 -0
  224. gobby/py.typed +0 -0
  225. gobby/runner.py +488 -0
  226. gobby/search/__init__.py +23 -0
  227. gobby/search/protocol.py +104 -0
  228. gobby/search/tfidf.py +232 -0
  229. gobby/servers/__init__.py +7 -0
  230. gobby/servers/http.py +636 -0
  231. gobby/servers/models.py +31 -0
  232. gobby/servers/routes/__init__.py +23 -0
  233. gobby/servers/routes/admin.py +416 -0
  234. gobby/servers/routes/dependencies.py +118 -0
  235. gobby/servers/routes/mcp/__init__.py +24 -0
  236. gobby/servers/routes/mcp/hooks.py +135 -0
  237. gobby/servers/routes/mcp/plugins.py +121 -0
  238. gobby/servers/routes/mcp/tools.py +1337 -0
  239. gobby/servers/routes/mcp/webhooks.py +159 -0
  240. gobby/servers/routes/sessions.py +582 -0
  241. gobby/servers/websocket.py +766 -0
  242. gobby/sessions/__init__.py +13 -0
  243. gobby/sessions/analyzer.py +322 -0
  244. gobby/sessions/lifecycle.py +240 -0
  245. gobby/sessions/manager.py +563 -0
  246. gobby/sessions/processor.py +225 -0
  247. gobby/sessions/summary.py +532 -0
  248. gobby/sessions/transcripts/__init__.py +41 -0
  249. gobby/sessions/transcripts/base.py +125 -0
  250. gobby/sessions/transcripts/claude.py +386 -0
  251. gobby/sessions/transcripts/codex.py +143 -0
  252. gobby/sessions/transcripts/gemini.py +195 -0
  253. gobby/storage/__init__.py +21 -0
  254. gobby/storage/agents.py +409 -0
  255. gobby/storage/artifact_classifier.py +341 -0
  256. gobby/storage/artifacts.py +285 -0
  257. gobby/storage/compaction.py +67 -0
  258. gobby/storage/database.py +357 -0
  259. gobby/storage/inter_session_messages.py +194 -0
  260. gobby/storage/mcp.py +680 -0
  261. gobby/storage/memories.py +562 -0
  262. gobby/storage/merge_resolutions.py +550 -0
  263. gobby/storage/migrations.py +860 -0
  264. gobby/storage/migrations_legacy.py +1359 -0
  265. gobby/storage/projects.py +166 -0
  266. gobby/storage/session_messages.py +251 -0
  267. gobby/storage/session_tasks.py +97 -0
  268. gobby/storage/sessions.py +817 -0
  269. gobby/storage/task_dependencies.py +223 -0
  270. gobby/storage/tasks/__init__.py +42 -0
  271. gobby/storage/tasks/_aggregates.py +180 -0
  272. gobby/storage/tasks/_crud.py +449 -0
  273. gobby/storage/tasks/_id.py +104 -0
  274. gobby/storage/tasks/_lifecycle.py +311 -0
  275. gobby/storage/tasks/_manager.py +889 -0
  276. gobby/storage/tasks/_models.py +300 -0
  277. gobby/storage/tasks/_ordering.py +119 -0
  278. gobby/storage/tasks/_path_cache.py +110 -0
  279. gobby/storage/tasks/_queries.py +343 -0
  280. gobby/storage/tasks/_search.py +143 -0
  281. gobby/storage/workflow_audit.py +393 -0
  282. gobby/storage/worktrees.py +547 -0
  283. gobby/sync/__init__.py +29 -0
  284. gobby/sync/github.py +333 -0
  285. gobby/sync/linear.py +304 -0
  286. gobby/sync/memories.py +284 -0
  287. gobby/sync/tasks.py +641 -0
  288. gobby/tasks/__init__.py +8 -0
  289. gobby/tasks/build_verification.py +193 -0
  290. gobby/tasks/commits.py +633 -0
  291. gobby/tasks/context.py +747 -0
  292. gobby/tasks/criteria.py +342 -0
  293. gobby/tasks/enhanced_validator.py +226 -0
  294. gobby/tasks/escalation.py +263 -0
  295. gobby/tasks/expansion.py +626 -0
  296. gobby/tasks/external_validator.py +764 -0
  297. gobby/tasks/issue_extraction.py +171 -0
  298. gobby/tasks/prompts/expand.py +327 -0
  299. gobby/tasks/research.py +421 -0
  300. gobby/tasks/tdd.py +352 -0
  301. gobby/tasks/tree_builder.py +263 -0
  302. gobby/tasks/validation.py +712 -0
  303. gobby/tasks/validation_history.py +357 -0
  304. gobby/tasks/validation_models.py +89 -0
  305. gobby/tools/__init__.py +0 -0
  306. gobby/tools/summarizer.py +170 -0
  307. gobby/tui/__init__.py +5 -0
  308. gobby/tui/api_client.py +281 -0
  309. gobby/tui/app.py +327 -0
  310. gobby/tui/screens/__init__.py +25 -0
  311. gobby/tui/screens/agents.py +333 -0
  312. gobby/tui/screens/chat.py +450 -0
  313. gobby/tui/screens/dashboard.py +377 -0
  314. gobby/tui/screens/memory.py +305 -0
  315. gobby/tui/screens/metrics.py +231 -0
  316. gobby/tui/screens/orchestrator.py +904 -0
  317. gobby/tui/screens/sessions.py +412 -0
  318. gobby/tui/screens/tasks.py +442 -0
  319. gobby/tui/screens/workflows.py +289 -0
  320. gobby/tui/screens/worktrees.py +174 -0
  321. gobby/tui/widgets/__init__.py +21 -0
  322. gobby/tui/widgets/chat.py +210 -0
  323. gobby/tui/widgets/conductor.py +104 -0
  324. gobby/tui/widgets/menu.py +132 -0
  325. gobby/tui/widgets/message_panel.py +160 -0
  326. gobby/tui/widgets/review_gate.py +224 -0
  327. gobby/tui/widgets/task_tree.py +99 -0
  328. gobby/tui/widgets/token_budget.py +166 -0
  329. gobby/tui/ws_client.py +258 -0
  330. gobby/utils/__init__.py +3 -0
  331. gobby/utils/daemon_client.py +235 -0
  332. gobby/utils/git.py +222 -0
  333. gobby/utils/id.py +38 -0
  334. gobby/utils/json_helpers.py +161 -0
  335. gobby/utils/logging.py +376 -0
  336. gobby/utils/machine_id.py +135 -0
  337. gobby/utils/metrics.py +589 -0
  338. gobby/utils/project_context.py +182 -0
  339. gobby/utils/project_init.py +263 -0
  340. gobby/utils/status.py +256 -0
  341. gobby/utils/validation.py +80 -0
  342. gobby/utils/version.py +23 -0
  343. gobby/workflows/__init__.py +4 -0
  344. gobby/workflows/actions.py +1310 -0
  345. gobby/workflows/approval_flow.py +138 -0
  346. gobby/workflows/artifact_actions.py +103 -0
  347. gobby/workflows/audit_helpers.py +110 -0
  348. gobby/workflows/autonomous_actions.py +286 -0
  349. gobby/workflows/context_actions.py +394 -0
  350. gobby/workflows/definitions.py +130 -0
  351. gobby/workflows/detection_helpers.py +208 -0
  352. gobby/workflows/engine.py +485 -0
  353. gobby/workflows/evaluator.py +669 -0
  354. gobby/workflows/git_utils.py +96 -0
  355. gobby/workflows/hooks.py +169 -0
  356. gobby/workflows/lifecycle_evaluator.py +613 -0
  357. gobby/workflows/llm_actions.py +70 -0
  358. gobby/workflows/loader.py +333 -0
  359. gobby/workflows/mcp_actions.py +60 -0
  360. gobby/workflows/memory_actions.py +272 -0
  361. gobby/workflows/premature_stop.py +164 -0
  362. gobby/workflows/session_actions.py +139 -0
  363. gobby/workflows/state_actions.py +123 -0
  364. gobby/workflows/state_manager.py +104 -0
  365. gobby/workflows/stop_signal_actions.py +163 -0
  366. gobby/workflows/summary_actions.py +344 -0
  367. gobby/workflows/task_actions.py +249 -0
  368. gobby/workflows/task_enforcement_actions.py +901 -0
  369. gobby/workflows/templates.py +52 -0
  370. gobby/workflows/todo_actions.py +84 -0
  371. gobby/workflows/webhook.py +223 -0
  372. gobby/workflows/webhook_executor.py +399 -0
  373. gobby/worktrees/__init__.py +5 -0
  374. gobby/worktrees/git.py +690 -0
  375. gobby/worktrees/merge/__init__.py +20 -0
  376. gobby/worktrees/merge/conflict_parser.py +177 -0
  377. gobby/worktrees/merge/resolver.py +485 -0
  378. gobby-0.2.5.dist-info/METADATA +351 -0
  379. gobby-0.2.5.dist-info/RECORD +383 -0
  380. gobby-0.2.5.dist-info/WHEEL +5 -0
  381. gobby-0.2.5.dist-info/entry_points.txt +2 -0
  382. gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
  383. gobby-0.2.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,380 @@
1
+ """Task orchestration tools: monitor (poll_agent_status, get_orchestration_status)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from gobby.mcp_proxy.tools.internal import InternalToolRegistry
9
+ from gobby.storage.tasks import TaskNotFoundError
10
+
11
+ from .utils import get_current_project_id
12
+
13
+ if TYPE_CHECKING:
14
+ from gobby.agents.runner import AgentRunner
15
+ from gobby.storage.tasks import LocalTaskManager
16
+ from gobby.storage.worktrees import LocalWorktreeManager
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def register_monitor(
22
+ registry: InternalToolRegistry,
23
+ task_manager: LocalTaskManager,
24
+ worktree_storage: LocalWorktreeManager,
25
+ agent_runner: AgentRunner | None = None,
26
+ default_project_id: str | None = None,
27
+ ) -> None:
28
+ """Register monitor tools."""
29
+ from gobby.mcp_proxy.tools.tasks import resolve_task_id_for_mcp
30
+
31
+ async def get_orchestration_status(
32
+ parent_task_id: str,
33
+ project_path: str | None = None,
34
+ ) -> dict[str, Any]:
35
+ """
36
+ Get the current orchestration status for a parent task.
37
+
38
+ Returns information about spawned agents, their status, and worktree state.
39
+
40
+ Args:
41
+ parent_task_id: Task reference: #N, N (seq_num), path (1.2.3), or UUID
42
+ project_path: Path to project directory
43
+
44
+ Returns:
45
+ Dict with orchestration status
46
+ """
47
+ # Resolve parent_task_id reference
48
+ try:
49
+ resolved_parent_task_id = resolve_task_id_for_mcp(task_manager, parent_task_id)
50
+ except (TaskNotFoundError, ValueError) as e:
51
+ return {
52
+ "success": False,
53
+ "error": f"Invalid parent_task_id: {e}",
54
+ }
55
+
56
+ # Resolve project ID
57
+ resolved_project_id = default_project_id
58
+ if project_path:
59
+ from pathlib import Path
60
+
61
+ from gobby.utils.project_context import get_project_context
62
+
63
+ ctx = get_project_context(Path(project_path))
64
+ if ctx:
65
+ resolved_project_id = ctx.get("id")
66
+
67
+ if not resolved_project_id:
68
+ resolved_project_id = get_current_project_id()
69
+
70
+ if not resolved_project_id:
71
+ return {
72
+ "success": False,
73
+ "error": "Could not resolve project ID",
74
+ }
75
+
76
+ # Get subtasks
77
+ subtasks = task_manager.list_tasks(parent_task_id=resolved_parent_task_id, limit=100)
78
+
79
+ # Categorize by status
80
+ open_tasks = []
81
+ in_progress_tasks = []
82
+ review_tasks: list[dict[str, Any]] = []
83
+ closed_tasks = []
84
+
85
+ for task in subtasks:
86
+ task_info: dict[str, Any] = {
87
+ "id": task.id,
88
+ "title": task.title,
89
+ "status": task.status,
90
+ "validation_status": task.validation_status,
91
+ }
92
+
93
+ # Check for linked worktree
94
+ worktree = worktree_storage.get_by_task(task.id)
95
+ if worktree:
96
+ task_info["worktree_id"] = worktree.id
97
+ task_info["worktree_status"] = worktree.status
98
+ task_info["has_active_agent"] = worktree.agent_session_id is not None
99
+
100
+ if task.status == "closed":
101
+ closed_tasks.append(task_info)
102
+ elif task.status == "in_progress":
103
+ in_progress_tasks.append(task_info)
104
+ elif task.status == "review":
105
+ review_tasks.append(task_info)
106
+ else:
107
+ open_tasks.append(task_info)
108
+
109
+ # Check if parent task is complete
110
+ parent_task = task_manager.get_task(resolved_parent_task_id)
111
+ is_complete = parent_task and parent_task.status == "closed"
112
+
113
+ return {
114
+ "success": True,
115
+ "parent_task_id": resolved_parent_task_id,
116
+ "is_complete": is_complete,
117
+ "summary": {
118
+ "open": len(open_tasks),
119
+ "in_progress": len(in_progress_tasks),
120
+ "review": len(review_tasks),
121
+ "closed": len(closed_tasks),
122
+ "total": len(subtasks),
123
+ },
124
+ "open_tasks": open_tasks,
125
+ "in_progress_tasks": in_progress_tasks,
126
+ "review_tasks": review_tasks,
127
+ "closed_tasks": closed_tasks,
128
+ }
129
+
130
+ registry.register(
131
+ name="get_orchestration_status",
132
+ description="Get current orchestration status for a parent task",
133
+ input_schema={
134
+ "type": "object",
135
+ "properties": {
136
+ "parent_task_id": {
137
+ "type": "string",
138
+ "description": "Task reference: #N, N (seq_num), path (1.2.3), or UUID",
139
+ },
140
+ "project_path": {
141
+ "type": "string",
142
+ "description": "Path to project directory",
143
+ },
144
+ },
145
+ "required": ["parent_task_id"],
146
+ },
147
+ func=get_orchestration_status,
148
+ )
149
+
150
+ async def poll_agent_status(
151
+ parent_session_id: str,
152
+ ) -> dict[str, Any]:
153
+ """
154
+ Poll running agents and update tracking lists in workflow state.
155
+
156
+ Checks all spawned agents for completion/failure and moves them to
157
+ appropriate lists (completed_agents, failed_agents). Used by the
158
+ auto-orchestrator workflow's monitor step.
159
+
160
+ Args:
161
+ parent_session_id: Parent session ID (orchestrator session)
162
+
163
+ Returns:
164
+ Dict with:
165
+ - still_running: List of agents still in progress
166
+ - newly_completed: List of agents that completed since last poll
167
+ - newly_failed: List of agents that failed since last poll
168
+ - summary: Counts of running/completed/failed
169
+ """
170
+ if agent_runner is None:
171
+ return {
172
+ "success": False,
173
+ "error": "Agent runner not configured",
174
+ }
175
+
176
+ # Get workflow state
177
+ from gobby.workflows.state_manager import WorkflowStateManager
178
+
179
+ state_manager = WorkflowStateManager(task_manager.db)
180
+ state = state_manager.get_state(parent_session_id)
181
+ if not state:
182
+ return {
183
+ "success": True,
184
+ "still_running": [],
185
+ "newly_completed": [],
186
+ "newly_failed": [],
187
+ "summary": {"running": 0, "completed": 0, "failed": 0},
188
+ "message": "No workflow state found",
189
+ }
190
+
191
+ workflow_vars = state.variables
192
+ spawned_agents = workflow_vars.get("spawned_agents", [])
193
+ completed_agents = workflow_vars.get("completed_agents", [])
194
+ failed_agents = workflow_vars.get("failed_agents", [])
195
+
196
+ if not spawned_agents:
197
+ return {
198
+ "success": True,
199
+ "still_running": [],
200
+ "newly_completed": completed_agents,
201
+ "newly_failed": failed_agents,
202
+ "summary": {
203
+ "running": 0,
204
+ "completed": len(completed_agents),
205
+ "failed": len(failed_agents),
206
+ },
207
+ "message": "No spawned agents to poll",
208
+ }
209
+
210
+ # Check status of each spawned agent
211
+ still_running: list[dict[str, Any]] = []
212
+ newly_completed: list[dict[str, Any]] = []
213
+ newly_failed: list[dict[str, Any]] = []
214
+
215
+ for agent_info in spawned_agents:
216
+ session_id = agent_info.get("session_id")
217
+ task_id = agent_info.get("task_id")
218
+
219
+ if not session_id:
220
+ # Invalid agent info, mark as failed
221
+ newly_failed.append(
222
+ {
223
+ **agent_info,
224
+ "failure_reason": "Missing session_id in agent info",
225
+ }
226
+ )
227
+ continue
228
+
229
+ # Check if the task is closed (agent completed successfully)
230
+ if task_id:
231
+ try:
232
+ task = task_manager.get_task(task_id)
233
+ except Exception as e:
234
+ logger.warning(f"Failed to get task {task_id}: {e}")
235
+ task = None
236
+
237
+ if task is not None and task.status == "closed":
238
+ newly_completed.append(
239
+ {
240
+ **agent_info,
241
+ "completed_at": task.closed_at,
242
+ "closed_reason": task.closed_reason,
243
+ "commit_sha": task.closed_commit_sha,
244
+ }
245
+ )
246
+ continue
247
+
248
+ # Check worktree status (if agent released worktree, it's done)
249
+ worktree_id = agent_info.get("worktree_id")
250
+ if worktree_id:
251
+ worktree = worktree_storage.get(worktree_id)
252
+ if worktree and not worktree.agent_session_id:
253
+ # Worktree released but task not closed - check if failed
254
+ if task_id:
255
+ task = task_manager.get_task(task_id)
256
+ if task and task.status != "closed":
257
+ # Agent released worktree without closing task
258
+ newly_failed.append(
259
+ {
260
+ **agent_info,
261
+ "failure_reason": "Agent released worktree without closing task",
262
+ }
263
+ )
264
+ continue
265
+
266
+ # Check if agent is still running via in-memory registry
267
+ running_agent = agent_runner.get_running_agent(session_id)
268
+ if running_agent:
269
+ # Still running
270
+ still_running.append(
271
+ {
272
+ **agent_info,
273
+ "running_since": running_agent.started_at.isoformat()
274
+ if running_agent.started_at
275
+ else None,
276
+ }
277
+ )
278
+ else:
279
+ # Agent not in running registry and task not closed
280
+ # Could be completed or failed - check task status
281
+ if task_id:
282
+ task = task_manager.get_task(task_id)
283
+ if task and task.status == "closed":
284
+ newly_completed.append(
285
+ {
286
+ **agent_info,
287
+ "completed_at": task.closed_at,
288
+ }
289
+ )
290
+ elif task and task.status == "in_progress":
291
+ # Still in progress but agent not running - likely crashed
292
+ newly_failed.append(
293
+ {
294
+ **agent_info,
295
+ "failure_reason": "Agent exited without completing task",
296
+ }
297
+ )
298
+ else:
299
+ # Task open, agent not running - was never started properly
300
+ newly_failed.append(
301
+ {
302
+ **agent_info,
303
+ "failure_reason": "Agent not running and task not started",
304
+ }
305
+ )
306
+ else:
307
+ # No task ID, can't determine status
308
+ newly_failed.append(
309
+ {
310
+ **agent_info,
311
+ "failure_reason": "Unknown status - no task_id",
312
+ }
313
+ )
314
+
315
+ # Update workflow state
316
+ # Compare by session IDs to detect real changes in agent membership
317
+ # (dict comparison would fail due to added fields like running_since)
318
+ still_running_ids = {a.get("session_id") for a in still_running}
319
+ spawned_ids = {a.get("session_id") for a in spawned_agents}
320
+ agents_changed = still_running_ids != spawned_ids
321
+
322
+ if newly_completed or newly_failed or agents_changed:
323
+ try:
324
+ # Re-fetch state to ensure we have the latest
325
+ state = state_manager.get_state(parent_session_id)
326
+ if state:
327
+ # Update completed_agents list
328
+ if newly_completed:
329
+ existing_completed = state.variables.get("completed_agents", [])
330
+ existing_completed.extend(newly_completed)
331
+ state.variables["completed_agents"] = existing_completed
332
+ completed_agents = existing_completed
333
+
334
+ # Update failed_agents list
335
+ if newly_failed:
336
+ existing_failed = state.variables.get("failed_agents", [])
337
+ existing_failed.extend(newly_failed)
338
+ state.variables["failed_agents"] = existing_failed
339
+ failed_agents = existing_failed
340
+
341
+ # Update spawned_agents to only include still running
342
+ state.variables["spawned_agents"] = still_running
343
+
344
+ state_manager.save_state(state)
345
+
346
+ except Exception as e:
347
+ logger.warning(f"Failed to update workflow state during poll: {e}")
348
+
349
+ return {
350
+ "success": True,
351
+ "still_running": still_running,
352
+ "newly_completed": newly_completed,
353
+ "newly_failed": newly_failed,
354
+ "summary": {
355
+ "running": len(still_running),
356
+ "completed": len(completed_agents),
357
+ "failed": len(failed_agents),
358
+ },
359
+ "all_done": len(still_running) == 0,
360
+ }
361
+
362
+ registry.register(
363
+ name="poll_agent_status",
364
+ description=(
365
+ "Poll running agents and update tracking lists. "
366
+ "Checks spawned_agents, moves completed to completed_agents and failed to failed_agents. "
367
+ "Used by auto-orchestrator monitor step."
368
+ ),
369
+ input_schema={
370
+ "type": "object",
371
+ "properties": {
372
+ "parent_session_id": {
373
+ "type": "string",
374
+ "description": "Parent session ID (orchestrator session)",
375
+ },
376
+ },
377
+ "required": ["parent_session_id"],
378
+ },
379
+ func=poll_agent_status,
380
+ )