devflow-engine 1.0.0__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 (393) hide show
  1. devflow_engine/__init__.py +3 -0
  2. devflow_engine/agentic_prompts.py +100 -0
  3. devflow_engine/agentic_runtime.py +398 -0
  4. devflow_engine/api_key_flow_harness.py +539 -0
  5. devflow_engine/api_keys.py +357 -0
  6. devflow_engine/bootstrap/__init__.py +2 -0
  7. devflow_engine/bootstrap/provision_from_template.py +84 -0
  8. devflow_engine/cli/__init__.py +0 -0
  9. devflow_engine/cli/app.py +7270 -0
  10. devflow_engine/core/__init__.py +0 -0
  11. devflow_engine/core/config.py +86 -0
  12. devflow_engine/core/logging.py +29 -0
  13. devflow_engine/core/paths.py +45 -0
  14. devflow_engine/core/toml_kv.py +33 -0
  15. devflow_engine/devflow_event_worker.py +1292 -0
  16. devflow_engine/devflow_state.py +201 -0
  17. devflow_engine/devin2/__init__.py +9 -0
  18. devflow_engine/devin2/agent_definition.py +120 -0
  19. devflow_engine/devin2/pi_runner.py +204 -0
  20. devflow_engine/devin_orchestration.py +69 -0
  21. devflow_engine/docs/prompts/anti-patterns.md +42 -0
  22. devflow_engine/docs/prompts/devin-agent-prompt.md +55 -0
  23. devflow_engine/docs/prompts/devin2-agent-prompt.md +81 -0
  24. devflow_engine/docs/prompts/examples/devin-vapi-clone-reference-exchange.json +85 -0
  25. devflow_engine/doctor/__init__.py +2 -0
  26. devflow_engine/doctor/triage.py +140 -0
  27. devflow_engine/error/__init__.py +0 -0
  28. devflow_engine/error/remediation.py +21 -0
  29. devflow_engine/errors/error_solver_dag.py +522 -0
  30. devflow_engine/errors/runtime_observability.py +67 -0
  31. devflow_engine/idea/__init__.py +4 -0
  32. devflow_engine/idea/actors.py +481 -0
  33. devflow_engine/idea/agentic.py +465 -0
  34. devflow_engine/idea/analyze.py +93 -0
  35. devflow_engine/idea/devin_chat_dag.py +1 -0
  36. devflow_engine/idea/diff.py +99 -0
  37. devflow_engine/idea/drafts.py +446 -0
  38. devflow_engine/idea/idea_creation_dag.py +643 -0
  39. devflow_engine/idea/ideation_enrichment.py +355 -0
  40. devflow_engine/idea/ideation_enrichment_worker.py +19 -0
  41. devflow_engine/idea/paths.py +28 -0
  42. devflow_engine/idea/promote.py +53 -0
  43. devflow_engine/idea/redaction.py +27 -0
  44. devflow_engine/idea/repo_tools.py +1277 -0
  45. devflow_engine/idea/response_mode.py +30 -0
  46. devflow_engine/idea/story_pipeline.py +1585 -0
  47. devflow_engine/idea/sufficiency.py +376 -0
  48. devflow_engine/idea/traditional_stories.py +1257 -0
  49. devflow_engine/implementation/__init__.py +0 -0
  50. devflow_engine/implementation/alembic_preflight.py +700 -0
  51. devflow_engine/implementation/dag.py +8450 -0
  52. devflow_engine/implementation/green_gate.py +93 -0
  53. devflow_engine/implementation/prompts.py +108 -0
  54. devflow_engine/implementation/test_runtime.py +623 -0
  55. devflow_engine/integration/__init__.py +19 -0
  56. devflow_engine/integration/agentic.py +66 -0
  57. devflow_engine/integration/dag.py +3539 -0
  58. devflow_engine/integration/prompts.py +114 -0
  59. devflow_engine/integration/supabase_schema.sql +31 -0
  60. devflow_engine/integration/supabase_sync.py +177 -0
  61. devflow_engine/llm/__init__.py +1 -0
  62. devflow_engine/llm/cli_one_shot.py +84 -0
  63. devflow_engine/llm/cli_stream.py +371 -0
  64. devflow_engine/llm/execution_context.py +26 -0
  65. devflow_engine/llm/invoke.py +1322 -0
  66. devflow_engine/llm/provider_api.py +304 -0
  67. devflow_engine/llm/repo_knowledge.py +588 -0
  68. devflow_engine/llm_primitives.py +315 -0
  69. devflow_engine/orchestration.py +62 -0
  70. devflow_engine/planning/__init__.py +0 -0
  71. devflow_engine/planning/analyze_repo.py +92 -0
  72. devflow_engine/planning/render_drafts.py +133 -0
  73. devflow_engine/playground/__init__.py +0 -0
  74. devflow_engine/playground/hooks.py +26 -0
  75. devflow_engine/playwright_workflow/__init__.py +5 -0
  76. devflow_engine/playwright_workflow/dag.py +1317 -0
  77. devflow_engine/process/__init__.py +5 -0
  78. devflow_engine/process/dag.py +59 -0
  79. devflow_engine/project_registration/__init__.py +3 -0
  80. devflow_engine/project_registration/dag.py +1581 -0
  81. devflow_engine/project_registry.py +109 -0
  82. devflow_engine/prompts/devin/generic/prompt.md +6 -0
  83. devflow_engine/prompts/devin/ideation/prompt.md +263 -0
  84. devflow_engine/prompts/devin/ideation/scenarios.md +5 -0
  85. devflow_engine/prompts/devin/ideation_loop/prompt.md +6 -0
  86. devflow_engine/prompts/devin/insight/prompt.md +11 -0
  87. devflow_engine/prompts/devin/insight/scenarios.md +5 -0
  88. devflow_engine/prompts/devin/intake/prompt.md +15 -0
  89. devflow_engine/prompts/devin/iterate/prompt.md +12 -0
  90. devflow_engine/prompts/devin/shared/eval_doctrine.md +9 -0
  91. devflow_engine/prompts/devin/shared/principles.md +246 -0
  92. devflow_engine/prompts/devin_eval/assessment/prompt.md +18 -0
  93. devflow_engine/prompts/idea/api_ideation_agent/prompt.md +8 -0
  94. devflow_engine/prompts/idea/api_insight_agent/prompt.md +8 -0
  95. devflow_engine/prompts/idea/response_doctrine/prompt.md +18 -0
  96. devflow_engine/prompts/implementation/dependency_assessment/prompt.md +12 -0
  97. devflow_engine/prompts/implementation/green/green/prompt.md +11 -0
  98. devflow_engine/prompts/implementation/green/node_config/prompt.md +3 -0
  99. devflow_engine/prompts/implementation/green_review/outcome_review/prompt.md +5 -0
  100. devflow_engine/prompts/implementation/green_review/prior_run_review/prompt.md +5 -0
  101. devflow_engine/prompts/implementation/red/prompt.md +27 -0
  102. devflow_engine/prompts/implementation/redreview/prompt.md +23 -0
  103. devflow_engine/prompts/implementation/redreview_repair/prompt.md +16 -0
  104. devflow_engine/prompts/implementation/setupdoc/prompt.md +10 -0
  105. devflow_engine/prompts/implementation/story_planning/prompt.md +13 -0
  106. devflow_engine/prompts/implementation/test_design/prompt.md +27 -0
  107. devflow_engine/prompts/integration/README.md +185 -0
  108. devflow_engine/prompts/integration/green/example.md +67 -0
  109. devflow_engine/prompts/integration/green/green/prompt.md +10 -0
  110. devflow_engine/prompts/integration/green/node_config/prompt.md +42 -0
  111. devflow_engine/prompts/integration/green/past_prompts/20260417T212300/green/prompt.md +15 -0
  112. devflow_engine/prompts/integration/green/past_prompts/20260417T212300/node_config/prompt.md +42 -0
  113. devflow_engine/prompts/integration/green_enrich/example.md +79 -0
  114. devflow_engine/prompts/integration/green_enrich/green_enrich/prompt.md +9 -0
  115. devflow_engine/prompts/integration/green_enrich/node_config/prompt.md +41 -0
  116. devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/green_enrich/prompt.md +14 -0
  117. devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/node_config/prompt.md +41 -0
  118. devflow_engine/prompts/integration/red/code_repair/prompt.md +12 -0
  119. devflow_engine/prompts/integration/red/example.md +152 -0
  120. devflow_engine/prompts/integration/red/node_config/prompt.md +86 -0
  121. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
  122. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/node_config/prompt.md +84 -0
  123. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red/prompt.md +16 -0
  124. devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red_repair/prompt.md +15 -0
  125. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
  126. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/node_config/prompt.md +84 -0
  127. devflow_engine/prompts/integration/red/past_prompts/20260417T215032/red_repair/prompt.md +11 -0
  128. devflow_engine/prompts/integration/red/red/prompt.md +11 -0
  129. devflow_engine/prompts/integration/red/red_repair/prompt.md +12 -0
  130. devflow_engine/prompts/integration/red_review/example.md +71 -0
  131. devflow_engine/prompts/integration/red_review/node_config/prompt.md +41 -0
  132. devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/node_config/prompt.md +41 -0
  133. devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/red_review/prompt.md +15 -0
  134. devflow_engine/prompts/integration/red_review/red_review/prompt.md +9 -0
  135. devflow_engine/prompts/integration/resolve/example.md +111 -0
  136. devflow_engine/prompts/integration/resolve/node_config/prompt.md +64 -0
  137. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/node_config/prompt.md +64 -0
  138. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_implicated_users/prompt.md +15 -0
  139. devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_side_effects/prompt.md +15 -0
  140. devflow_engine/prompts/integration/resolve/resolve_implicated_users/prompt.md +10 -0
  141. devflow_engine/prompts/integration/resolve/resolve_side_effects/prompt.md +10 -0
  142. devflow_engine/prompts/integration/validate/build_idea_acceptance_coverage/prompt.md +12 -0
  143. devflow_engine/prompts/integration/validate/code_repair/prompt.md +13 -0
  144. devflow_engine/prompts/integration/validate/example.md +143 -0
  145. devflow_engine/prompts/integration/validate/node_config/prompt.md +87 -0
  146. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
  147. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/node_config/prompt.md +67 -0
  148. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_enrich_gate/prompt.md +17 -0
  149. devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_repair/prompt.md +16 -0
  150. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
  151. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/node_config/prompt.md +67 -0
  152. devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/validate_repair/prompt.md +9 -0
  153. devflow_engine/prompts/integration/validate/validate_enrich_gate/prompt.md +10 -0
  154. devflow_engine/prompts/integration/validate/validate_repair/prompt.md +20 -0
  155. devflow_engine/prompts/integration/write_workflows/example.md +100 -0
  156. devflow_engine/prompts/integration/write_workflows/node_config/prompt.md +44 -0
  157. devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/node_config/prompt.md +44 -0
  158. devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/write_workflows/prompt.md +17 -0
  159. devflow_engine/prompts/integration/write_workflows/write_workflows/prompt.md +11 -0
  160. devflow_engine/prompts/iterate/README.md +7 -0
  161. devflow_engine/prompts/iterate/coder/prompt.md +11 -0
  162. devflow_engine/prompts/iterate/framer/prompt.md +11 -0
  163. devflow_engine/prompts/iterate/iterator/prompt.md +13 -0
  164. devflow_engine/prompts/iterate/observer/prompt.md +11 -0
  165. devflow_engine/prompts/recovery/diagnosis/prompt.md +7 -0
  166. devflow_engine/prompts/recovery/execution/prompt.md +8 -0
  167. devflow_engine/prompts/recovery/execution_verification/prompt.md +7 -0
  168. devflow_engine/prompts/recovery/failure_investigation/prompt.md +10 -0
  169. devflow_engine/prompts/recovery/preflight_health_repo_repair/prompt.md +8 -0
  170. devflow_engine/prompts/recovery/remediation_execution/prompt.md +11 -0
  171. devflow_engine/prompts/recovery/root_cause_investigation/prompt.md +12 -0
  172. devflow_engine/prompts/scope_idea/doctrine/prompt.md +7 -0
  173. devflow_engine/prompts/source_doc_eval/document/prompt.md +6 -0
  174. devflow_engine/prompts/source_doc_eval/targeted_mutation/prompt.md +9 -0
  175. devflow_engine/prompts/source_doc_mutation/domain_entities/prompt.md +6 -0
  176. devflow_engine/prompts/source_doc_mutation/product_brief/prompt.md +6 -0
  177. devflow_engine/prompts/source_doc_mutation/project_doc_coherence/prompt.md +7 -0
  178. devflow_engine/prompts/source_doc_mutation/project_doc_render/prompt.md +9 -0
  179. devflow_engine/prompts/source_doc_mutation/source_doc_coherence/prompt.md +5 -0
  180. devflow_engine/prompts/source_doc_mutation/source_doc_enrichment_coherence/prompt.md +6 -0
  181. devflow_engine/prompts/source_doc_mutation/user_workflows/prompt.md +6 -0
  182. devflow_engine/prompts/source_scope/doctrine/prompt.md +10 -0
  183. devflow_engine/prompts/ui_grounding/doctrine/prompt.md +7 -0
  184. devflow_engine/recovery/__init__.py +3 -0
  185. devflow_engine/recovery/dag.py +2609 -0
  186. devflow_engine/recovery/models.py +220 -0
  187. devflow_engine/refactor.py +93 -0
  188. devflow_engine/registry/__init__.py +1 -0
  189. devflow_engine/registry/cards.py +238 -0
  190. devflow_engine/registry/domain_normalize.py +60 -0
  191. devflow_engine/registry/effects.py +65 -0
  192. devflow_engine/registry/enforce_report.py +150 -0
  193. devflow_engine/registry/module_cards_classify.py +164 -0
  194. devflow_engine/registry/module_cards_draft.py +184 -0
  195. devflow_engine/registry/module_cards_gate.py +59 -0
  196. devflow_engine/registry/packages.py +347 -0
  197. devflow_engine/registry/pathways.py +323 -0
  198. devflow_engine/review/__init__.py +11 -0
  199. devflow_engine/review/dag.py +588 -0
  200. devflow_engine/review/review_story.py +67 -0
  201. devflow_engine/scope_idea/__init__.py +3 -0
  202. devflow_engine/scope_idea/agentic.py +39 -0
  203. devflow_engine/scope_idea/dag.py +1069 -0
  204. devflow_engine/scope_idea/models.py +175 -0
  205. devflow_engine/skills/builtins/devflow/queue_failure_investigation/SKILL.md +112 -0
  206. devflow_engine/skills/builtins/devflow/queue_idea_to_story/SKILL.md +120 -0
  207. devflow_engine/skills/builtins/devflow/queue_integration/SKILL.md +105 -0
  208. devflow_engine/skills/builtins/devflow/queue_recovery/SKILL.md +108 -0
  209. devflow_engine/skills/builtins/devflow/queue_runtime_core/SKILL.md +155 -0
  210. devflow_engine/skills/builtins/devflow/queue_story_implementation/SKILL.md +122 -0
  211. devflow_engine/skills/builtins/devin/idea_to_story_handoff/SKILL.md +120 -0
  212. devflow_engine/skills/builtins/devin/ideation/SKILL.md +168 -0
  213. devflow_engine/skills/builtins/devin/ideation/state-and-phrasing-reference.md +18 -0
  214. devflow_engine/skills/builtins/devin/insight/SKILL.md +22 -0
  215. devflow_engine/skills/registry.example.yaml +42 -0
  216. devflow_engine/source_doc_assumptions.py +291 -0
  217. devflow_engine/source_doc_mutation_dag.py +1606 -0
  218. devflow_engine/source_doc_mutation_eval.py +417 -0
  219. devflow_engine/source_doc_mutation_worker.py +25 -0
  220. devflow_engine/source_docs_schema.py +207 -0
  221. devflow_engine/source_docs_updater.py +309 -0
  222. devflow_engine/source_scope/__init__.py +15 -0
  223. devflow_engine/source_scope/agentic.py +45 -0
  224. devflow_engine/source_scope/dag.py +1626 -0
  225. devflow_engine/source_scope/models.py +177 -0
  226. devflow_engine/stores/__init__.py +0 -0
  227. devflow_engine/stores/execution_store.py +3534 -0
  228. devflow_engine/story/__init__.py +0 -0
  229. devflow_engine/story/contracts.py +160 -0
  230. devflow_engine/story/discovery.py +47 -0
  231. devflow_engine/story/evidence.py +118 -0
  232. devflow_engine/story/hashing.py +27 -0
  233. devflow_engine/story/implemented_queue_purge.py +148 -0
  234. devflow_engine/story/indexer.py +105 -0
  235. devflow_engine/story/io.py +20 -0
  236. devflow_engine/story/markdown_contracts.py +298 -0
  237. devflow_engine/story/reconciliation.py +408 -0
  238. devflow_engine/story/validate_stories.py +149 -0
  239. devflow_engine/story/validate_tests_story.py +512 -0
  240. devflow_engine/story/validation.py +133 -0
  241. devflow_engine/ui_grounding/__init__.py +11 -0
  242. devflow_engine/ui_grounding/agentic.py +31 -0
  243. devflow_engine/ui_grounding/dag.py +874 -0
  244. devflow_engine/ui_grounding/models.py +224 -0
  245. devflow_engine/ui_grounding/pencil_bridge.py +247 -0
  246. devflow_engine/vendor/__init__.py +0 -0
  247. devflow_engine/vendor/datalumina_genai/__init__.py +11 -0
  248. devflow_engine/vendor/datalumina_genai/core/__init__.py +0 -0
  249. devflow_engine/vendor/datalumina_genai/core/exceptions.py +9 -0
  250. devflow_engine/vendor/datalumina_genai/core/nodes/__init__.py +0 -0
  251. devflow_engine/vendor/datalumina_genai/core/nodes/agent.py +48 -0
  252. devflow_engine/vendor/datalumina_genai/core/nodes/agent_streaming_node.py +26 -0
  253. devflow_engine/vendor/datalumina_genai/core/nodes/base.py +89 -0
  254. devflow_engine/vendor/datalumina_genai/core/nodes/concurrent.py +30 -0
  255. devflow_engine/vendor/datalumina_genai/core/nodes/router.py +69 -0
  256. devflow_engine/vendor/datalumina_genai/core/schema.py +72 -0
  257. devflow_engine/vendor/datalumina_genai/core/task.py +52 -0
  258. devflow_engine/vendor/datalumina_genai/core/validate.py +139 -0
  259. devflow_engine/vendor/datalumina_genai/core/workflow.py +200 -0
  260. devflow_engine/worker.py +1086 -0
  261. devflow_engine/worker_guard.py +233 -0
  262. devflow_engine-1.0.0.dist-info/METADATA +235 -0
  263. devflow_engine-1.0.0.dist-info/RECORD +393 -0
  264. devflow_engine-1.0.0.dist-info/WHEEL +4 -0
  265. devflow_engine-1.0.0.dist-info/entry_points.txt +3 -0
  266. devin/__init__.py +6 -0
  267. devin/dag.py +58 -0
  268. devin/dag_two_arm.py +138 -0
  269. devin/devin_chat_scenario_catalog.json +588 -0
  270. devin/devin_eval.py +677 -0
  271. devin/nodes/__init__.py +0 -0
  272. devin/nodes/ideation/__init__.py +0 -0
  273. devin/nodes/ideation/node.py +195 -0
  274. devin/nodes/ideation/playground.py +267 -0
  275. devin/nodes/ideation/prompt.md +65 -0
  276. devin/nodes/ideation/scenarios/continue_refinement.py +13 -0
  277. devin/nodes/ideation/scenarios/continue_refinement_evals.py +18 -0
  278. devin/nodes/ideation/scenarios/idea_fits_existing_patterns.py +17 -0
  279. devin/nodes/ideation/scenarios/idea_fits_existing_patterns_evals.py +16 -0
  280. devin/nodes/ideation/scenarios/large_idea_split.py +4 -0
  281. devin/nodes/ideation/scenarios/large_idea_split_evals.py +17 -0
  282. devin/nodes/ideation/scenarios/source_documentation_added.py +4 -0
  283. devin/nodes/ideation/scenarios/source_documentation_added_evals.py +16 -0
  284. devin/nodes/ideation/scenarios/user_says_create_it.py +30 -0
  285. devin/nodes/ideation/scenarios/user_says_create_it_evals.py +23 -0
  286. devin/nodes/ideation/scenarios/vague_idea.py +16 -0
  287. devin/nodes/ideation/scenarios/vague_idea_evals.py +47 -0
  288. devin/nodes/ideation/tools.json +312 -0
  289. devin/nodes/insight/__init__.py +0 -0
  290. devin/nodes/insight/node.py +49 -0
  291. devin/nodes/insight/playground.py +154 -0
  292. devin/nodes/insight/prompt.md +61 -0
  293. devin/nodes/insight/scenarios/architecture_pattern_query.py +15 -0
  294. devin/nodes/insight/scenarios/architecture_pattern_query_evals.py +25 -0
  295. devin/nodes/insight/scenarios/codebase_exploration.py +15 -0
  296. devin/nodes/insight/scenarios/codebase_exploration_evals.py +23 -0
  297. devin/nodes/insight/scenarios/devin_ideation_routing.py +19 -0
  298. devin/nodes/insight/scenarios/devin_ideation_routing_evals.py +39 -0
  299. devin/nodes/insight/scenarios/devin_insight_routing.py +20 -0
  300. devin/nodes/insight/scenarios/devin_insight_routing_evals.py +40 -0
  301. devin/nodes/insight/scenarios/operational_debugging.py +15 -0
  302. devin/nodes/insight/scenarios/operational_debugging_evals.py +23 -0
  303. devin/nodes/insight/scenarios/operational_question.py +9 -0
  304. devin/nodes/insight/scenarios/operational_question_evals.py +8 -0
  305. devin/nodes/insight/scenarios/queue_status.py +15 -0
  306. devin/nodes/insight/scenarios/queue_status_evals.py +23 -0
  307. devin/nodes/insight/scenarios/source_doc_explanation.py +14 -0
  308. devin/nodes/insight/scenarios/source_doc_explanation_evals.py +21 -0
  309. devin/nodes/insight/scenarios/worker_state_check.py +15 -0
  310. devin/nodes/insight/scenarios/worker_state_check_evals.py +22 -0
  311. devin/nodes/insight/tools.json +126 -0
  312. devin/nodes/intake/__init__.py +0 -0
  313. devin/nodes/intake/node.py +27 -0
  314. devin/nodes/intake/playground.py +47 -0
  315. devin/nodes/intake/prompt.md +12 -0
  316. devin/nodes/intake/scenarios/ideation_routing.py +4 -0
  317. devin/nodes/intake/scenarios/ideation_routing_evals.py +5 -0
  318. devin/nodes/intake/scenarios/insight_routing.py +4 -0
  319. devin/nodes/intake/scenarios/insight_routing_evals.py +5 -0
  320. devin/nodes/iterate/README.md +44 -0
  321. devin/nodes/iterate/__init__.py +1 -0
  322. devin/nodes/iterate/_archived_design_stages/01-objectives-requirements.md +112 -0
  323. devin/nodes/iterate/_archived_design_stages/02-evals.md +131 -0
  324. devin/nodes/iterate/_archived_design_stages/03-tools-and-boundaries.md +110 -0
  325. devin/nodes/iterate/_archived_design_stages/04-harness-and-playground.md +32 -0
  326. devin/nodes/iterate/_archived_design_stages/05-prompt-deferred.md +11 -0
  327. devin/nodes/iterate/_archived_design_stages/coder_agent_design/01-objectives-requirements.md +20 -0
  328. devin/nodes/iterate/_archived_design_stages/coder_agent_design/02-evals.md +8 -0
  329. devin/nodes/iterate/_archived_design_stages/coder_agent_design/03-tools-and-boundaries.md +14 -0
  330. devin/nodes/iterate/_archived_design_stages/coder_agent_design/04-harness-and-playground.md +12 -0
  331. devin/nodes/iterate/_archived_design_stages/framer_agent_design/01-objectives-requirements.md +20 -0
  332. devin/nodes/iterate/_archived_design_stages/framer_agent_design/02-evals.md +8 -0
  333. devin/nodes/iterate/_archived_design_stages/framer_agent_design/03-tools-and-boundaries.md +13 -0
  334. devin/nodes/iterate/_archived_design_stages/framer_agent_design/04-harness-and-playground.md +12 -0
  335. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/01-objectives-requirements.md +25 -0
  336. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/02-evals.md +9 -0
  337. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/03-tools-and-boundaries.md +14 -0
  338. devin/nodes/iterate/_archived_design_stages/iterator_agent_design/04-harness-and-playground.md +12 -0
  339. devin/nodes/iterate/_archived_design_stages/observer_agent_design/01-objectives-requirements.md +20 -0
  340. devin/nodes/iterate/_archived_design_stages/observer_agent_design/02-evals.md +8 -0
  341. devin/nodes/iterate/_archived_design_stages/observer_agent_design/03-tools-and-boundaries.md +14 -0
  342. devin/nodes/iterate/_archived_design_stages/observer_agent_design/04-harness-and-playground.md +13 -0
  343. devin/nodes/iterate/agent-roles.md +89 -0
  344. devin/nodes/iterate/agents/README.md +10 -0
  345. devin/nodes/iterate/artifacts.md +504 -0
  346. devin/nodes/iterate/contract.md +100 -0
  347. devin/nodes/iterate/eval-plan.md +74 -0
  348. devin/nodes/iterate/node.py +100 -0
  349. devin/nodes/iterate/pipeline/README.md +13 -0
  350. devin/nodes/iterate/playground-contract.md +76 -0
  351. devin/nodes/iterate/prompt.md +11 -0
  352. devin/nodes/iterate/scenarios/README.md +38 -0
  353. devin/nodes/iterate/scenarios/artifact-and-loop-scenarios.md +101 -0
  354. devin/nodes/iterate/scenarios/coder_artifact_alignment.py +32 -0
  355. devin/nodes/iterate/scenarios/coder_artifact_alignment_evals.py +45 -0
  356. devin/nodes/iterate/scenarios/coder_bounded_fix.py +27 -0
  357. devin/nodes/iterate/scenarios/coder_bounded_fix_evals.py +45 -0
  358. devin/nodes/iterate/scenarios/devin_iterate_routing.py +21 -0
  359. devin/nodes/iterate/scenarios/devin_iterate_routing_evals.py +36 -0
  360. devin/nodes/iterate/scenarios/framer_scope_boundary.py +25 -0
  361. devin/nodes/iterate/scenarios/framer_scope_boundary_evals.py +57 -0
  362. devin/nodes/iterate/scenarios/framer_task_framing.py +25 -0
  363. devin/nodes/iterate/scenarios/framer_task_framing_evals.py +58 -0
  364. devin/nodes/iterate/scenarios/iterate_error_fix.py +21 -0
  365. devin/nodes/iterate/scenarios/iterate_error_fix_evals.py +39 -0
  366. devin/nodes/iterate/scenarios/iterate_quick_change.py +21 -0
  367. devin/nodes/iterate/scenarios/iterate_quick_change_evals.py +35 -0
  368. devin/nodes/iterate/scenarios/iterate_to_idea_promotion.py +23 -0
  369. devin/nodes/iterate/scenarios/iterate_to_idea_promotion_evals.py +53 -0
  370. devin/nodes/iterate/scenarios/iterate_to_insight_reroute.py +23 -0
  371. devin/nodes/iterate/scenarios/iterate_to_insight_reroute_evals.py +53 -0
  372. devin/nodes/iterate/scenarios/observer_evidence_seam.py +28 -0
  373. devin/nodes/iterate/scenarios/observer_evidence_seam_evals.py +55 -0
  374. devin/nodes/iterate/scenarios/observer_repro_creation.py +28 -0
  375. devin/nodes/iterate/scenarios/observer_repro_creation_evals.py +45 -0
  376. devin/nodes/iterate/scenarios/routing-matrix.md +45 -0
  377. devin/nodes/shared/__init__.py +0 -0
  378. devin/nodes/shared/filemaker_expert.md +80 -0
  379. devin/nodes/shared/filemaker_expert.py +354 -0
  380. devin/nodes/shared/filemaker_expert_eval/runner.py +176 -0
  381. devin/nodes/shared/filemaker_expert_eval/scenarios.json +65 -0
  382. devin/nodes/shared/goldilocks_advisor_eval/runner.py +214 -0
  383. devin/nodes/shared/goldilocks_advisor_eval/scenarios.json +58 -0
  384. devin/nodes/shared/helpers.py +156 -0
  385. devin/nodes/shared/idea_compliance_advisor_eval/runner.py +252 -0
  386. devin/nodes/shared/idea_compliance_advisor_eval/scenarios.json +75 -0
  387. devin/nodes/shared/models.py +44 -0
  388. devin/nodes/shared/post.py +40 -0
  389. devin/nodes/shared/router.py +107 -0
  390. devin/nodes/shared/tools.py +191 -0
  391. devin/shared/devin-chat-rubric.md +237 -0
  392. devin/shared/devin-chat-scenario-suite.md +90 -0
  393. devin/shared/eval_doctrine.md +9 -0
@@ -0,0 +1,69 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Optional, Type
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from ..task import TaskContext
7
+ from .base import Node
8
+
9
+ """
10
+ Router Module
11
+
12
+ This module implements the routing logic for workflow nodes.
13
+ It provides base classes for implementing routing decisions between nodes
14
+ in a processing workflow.
15
+ """
16
+
17
+
18
+ class BaseRouter(Node):
19
+ """Base router class for implementing node routing logic.
20
+
21
+ The BaseRouter class provides core routing functionality for directing
22
+ task flow between workflow nodes. It processes routing rules in sequence
23
+ and falls back to a default node if no rules match.
24
+
25
+ Attributes:
26
+ routes: List of RouterNode instances defining routing rules
27
+ fallback: Optional default node to route to if no rules match
28
+ """
29
+
30
+ async def process(self, task_context: TaskContext) -> TaskContext:
31
+ pass
32
+
33
+ def route(self, task_context: TaskContext) -> Node:
34
+ """Determines the next node based on routing rules.
35
+
36
+ Evaluates each routing rule in sequence and returns the first
37
+ matching node. Falls back to the default node if no rules match.
38
+
39
+ Args:
40
+ task_context: Current task execution context
41
+
42
+ Returns:
43
+ The next node to execute, or None if no route is found
44
+ """
45
+ for route_node in self.routes:
46
+ route_node.task_context = task_context
47
+ next_node = route_node.determine_next_node(task_context)
48
+ if next_node:
49
+ return next_node
50
+ return self.fallback if self.fallback else None
51
+
52
+
53
+ class RouterNode(ABC):
54
+ def __init__(self, task_context: TaskContext = None):
55
+ self.task_context = task_context
56
+
57
+ @abstractmethod
58
+ def determine_next_node(self, task_context: TaskContext) -> Optional[Node]:
59
+ pass
60
+
61
+ @property
62
+ def node_name(self):
63
+ return self.__class__.__name__
64
+
65
+ def save_output(self, output: BaseModel):
66
+ self.task_context.nodes[self.node_name] = output
67
+
68
+ def get_output(self, node_class: Type[Node]):
69
+ return self.task_context.nodes.get(node_class.__name__, None)
@@ -0,0 +1,72 @@
1
+ from typing import List, Type, Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from .nodes.base import Node
6
+
7
+ """
8
+ Workflow Schema Module
9
+
10
+ This module defines the schema classes used to configure workflow structures.
11
+ It provides a type-safe way to define node connections and workflow layouts
12
+ using Pydantic models.
13
+ """
14
+
15
+
16
+ class NodeConfig(BaseModel):
17
+ """Configuration model for workflow nodes.
18
+
19
+ NodeConfig defines the structure and behavior of a single node within
20
+ a workflow, including its connections to other nodes and routing properties.
21
+
22
+ Attributes:
23
+ node: The Node class to be instantiated
24
+ connections: List of Node classes this node can connect to
25
+ is_router: Flag indicating if this node performs routing logic
26
+ description: Optional description of the node's purpose
27
+ concurrent_nodes: Optional list of Node classes that can run concurrently
28
+
29
+ Example:
30
+ config = NodeConfig(
31
+ node=AnalyzeNode,
32
+ connections=[RouterNode],
33
+ is_router=False,
34
+ description="Analyzes incoming requests"
35
+ concurrent_nodes=[FilterContentGuardrailNode, FilterSQLInjectionGuardrailNode]
36
+ )
37
+ """
38
+
39
+ node: Type[Node]
40
+ connections: List[Type[Node]] = Field(default_factory=list)
41
+ is_router: bool = False
42
+ description: Optional[str] = None
43
+ concurrent_nodes: Optional[List[Type[Node]]] = Field(default_factory=list)
44
+
45
+
46
+ class WorkflowSchema(BaseModel):
47
+ """Schema definition for a complete workflow.
48
+
49
+ WorkflowSchema defines the overall structure of a processing workflow,
50
+ including its entry point and all constituent nodes.
51
+
52
+ Attributes:
53
+ description: Optional description of the workflow's purpose
54
+ event_schema: Pydantic model for validating incoming events
55
+ start: The entry point Node class for the workflow
56
+ nodes: List of NodeConfig objects defining the workflow structure
57
+
58
+ Example:
59
+ schema = WorkflowSchema(
60
+ description="Support ticket processing workflow",
61
+ start=AnalyzeNode,
62
+ nodes=[
63
+ NodeConfig(node=AnalyzeNode, connections=[RouterNode]),
64
+ NodeConfig(node=RouterNode, connections=[ResponseNode, EscalateNode]),
65
+ ]
66
+ )
67
+ """
68
+
69
+ description: Optional[str] = None
70
+ event_schema: Type[BaseModel]
71
+ start: Type[Node]
72
+ nodes: List[NodeConfig]
@@ -0,0 +1,52 @@
1
+ from typing import Any, Dict
2
+ from pydantic import BaseModel, Field
3
+
4
+ """
5
+ Task Context Module
6
+ This module defines the context object that gets passed between workflow nodes.
7
+ It maintains the state and metadata throughout workflow execution.
8
+ """
9
+
10
+
11
+ class TaskContext(BaseModel):
12
+ """Context container for workflow task execution.
13
+ TaskContext maintains the state and results of a workflow's execution,
14
+ tracking the original event, intermediate node results, and additional
15
+ metadata throughout the processing flow.
16
+ Attributes:
17
+ event: The original event that triggered the workflow
18
+ nodes: Dictionary storing results and state from each node's execution
19
+ metadata: Dictionary storing workflow-level metadata and configuration
20
+ should_stop: Boolean flag indicating whether the workflow should stop execution
21
+ Example:
22
+ context = TaskContext(
23
+ event=incoming_event,
24
+ nodes={"AnalyzeNode": {"score": 0.95}},
25
+ metadata={"priority": "high"}
26
+ )
27
+ """
28
+
29
+ event: Any
30
+ nodes: Dict[str, Any] = Field(
31
+ default_factory=dict,
32
+ description="Stores results and state from each node's execution",
33
+ )
34
+ metadata: Dict[str, Any] = Field(
35
+ default_factory=dict,
36
+ description="Stores workflow-level metadata and configuration",
37
+ )
38
+ should_stop: bool = Field(
39
+ default=False,
40
+ description="Flag indicating whether the workflow should stop execution",
41
+ )
42
+
43
+ def update_node(self, node_name: str, **kwargs):
44
+ self.nodes[node_name] = {**self.nodes.get(node_name, {}), **kwargs}
45
+
46
+ def stop_workflow(self) -> None:
47
+ """Stops the current workflow execution by setting the stop flag.
48
+
49
+ This method can be called from any node to halt the workflow processing.
50
+ Once called, the workflow will stop after the current node completes.
51
+ """
52
+ self.should_stop = True
@@ -0,0 +1,139 @@
1
+ from collections import deque
2
+ from typing import Set, Type
3
+
4
+ from .nodes.base import Node
5
+ from .schema import WorkflowSchema
6
+
7
+ """
8
+ Workflow Validator Module
9
+
10
+ This module provides validation logic for workflow schemas.
11
+ It ensures that workflows form valid directed acyclic graphs (DAGs)
12
+ and that routing configurations are correct.
13
+ """
14
+
15
+
16
+ class WorkflowValidator:
17
+ """Validator for ensuring workflow schema correctness.
18
+
19
+ The WorkflowValidator performs comprehensive validation of workflow schemas,
20
+ checking for cycles, unreachable nodes, and proper routing configurations.
21
+ It ensures that the workflow forms a valid directed acyclic graph (DAG)
22
+ and that routing nodes are properly configured.
23
+
24
+ Attributes:
25
+ workflow_schema: The WorkflowSchema to validate
26
+
27
+ Example:
28
+ validator = WorkflowValidator(workflow_schema)
29
+ validator.validate() # Raises ValueError if validation fails
30
+ """
31
+
32
+ def __init__(self, workflow_schema: WorkflowSchema):
33
+ """Initializes the validator with a workflow schema.
34
+
35
+ Args:
36
+ workflow_schema: The WorkflowSchema to validate
37
+ """
38
+ self.workflow_schema = workflow_schema
39
+
40
+ def validate(self):
41
+ """Validates all aspects of the workflow schema.
42
+
43
+ Performs comprehensive validation including DAG structure
44
+ and routing configuration checks.
45
+
46
+ Raises:
47
+ ValueError: If any validation check fails
48
+ """
49
+ self._validate_dag()
50
+ self._validate_connections()
51
+
52
+ def _validate_dag(self):
53
+ """Validates that the workflow schema forms a proper DAG.
54
+
55
+ Checks for cycles and ensures all nodes are reachable
56
+ from the start node.
57
+
58
+ Raises:
59
+ ValueError: If the workflow contains cycles or unreachable nodes
60
+ """
61
+ if self._has_cycle():
62
+ raise ValueError("Workflow schema contains a cycle")
63
+
64
+ reachable_nodes = self._get_reachable_nodes()
65
+ all_nodes = set(nc.node for nc in self.workflow_schema.nodes)
66
+ unreachable_nodes = all_nodes - reachable_nodes
67
+ if unreachable_nodes:
68
+ raise ValueError(
69
+ f"The following nodes are unreachable: {unreachable_nodes}"
70
+ )
71
+
72
+ def _has_cycle(self) -> bool:
73
+ """Detects cycles in the workflow graph using DFS.
74
+
75
+ Returns:
76
+ bool: True if a cycle is detected, False otherwise
77
+ """
78
+ visited = set()
79
+ rec_stack = set()
80
+
81
+ def dfs(node: Type[Node]) -> bool:
82
+ visited.add(node)
83
+ rec_stack.add(node)
84
+
85
+ node_config = next(
86
+ (nc for nc in self.workflow_schema.nodes if nc.node == node), None
87
+ )
88
+ if node_config:
89
+ for neighbor in node_config.connections:
90
+ if neighbor not in visited:
91
+ if dfs(neighbor):
92
+ return True
93
+ elif neighbor in rec_stack:
94
+ return True
95
+
96
+ rec_stack.remove(node)
97
+ return False
98
+
99
+ for node_config in self.workflow_schema.nodes:
100
+ if node_config.node not in visited:
101
+ if dfs(node_config.node):
102
+ return True
103
+
104
+ return False
105
+
106
+ def _get_reachable_nodes(self) -> Set[Type[Node]]:
107
+ """Identifies all nodes reachable from the start node using BFS.
108
+
109
+ Returns:
110
+ Set[Type[Node]]: Set of all reachable node classes
111
+ """
112
+ reachable = set()
113
+ queue = deque([self.workflow_schema.start])
114
+
115
+ while queue:
116
+ node = queue.popleft()
117
+ if node not in reachable:
118
+ reachable.add(node)
119
+ node_config = next(
120
+ (nc for nc in self.workflow_schema.nodes if nc.node == node), None
121
+ )
122
+ if node_config:
123
+ queue.extend(node_config.connections)
124
+
125
+ return reachable
126
+
127
+ def _validate_connections(self):
128
+ """Validates node connection configurations.
129
+
130
+ Ensures that only nodes marked as routers have multiple connections.
131
+
132
+ Raises:
133
+ ValueError: If a non-router node has multiple connections
134
+ """
135
+ for node_config in self.workflow_schema.nodes:
136
+ if len(node_config.connections) > 1 and not node_config.is_router:
137
+ raise ValueError(
138
+ f"Node {node_config.node.__name__} has multiple connections but is not marked as a router."
139
+ )
@@ -0,0 +1,200 @@
1
+ """Workflow orchestration (vendored from Datalumina GenAI).
2
+
3
+ This is a *minimal* subset used by DevFlow Engine.
4
+
5
+ Notable changes vs upstream:
6
+ - Removed Langfuse tracing + dotenv side-effects (keep engine self-contained)
7
+ - Normalized imports to be package-relative
8
+
9
+ The engine supports async execution and simple router nodes.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import logging
16
+ from abc import ABC
17
+ from contextlib import contextmanager, nullcontext
18
+ from typing import Any, AsyncIterator, ClassVar, Dict, Optional, Type
19
+
20
+ from .nodes.agent_streaming_node import AgentStreamingNode
21
+ from .nodes.base import Node
22
+ from .nodes.router import BaseRouter
23
+ from .schema import NodeConfig, WorkflowSchema
24
+ from .task import TaskContext
25
+ from .validate import WorkflowValidator
26
+
27
+
28
+ class NoOpSpan:
29
+ """No-op span for compatibility with upstream tracing hooks."""
30
+
31
+ def update(self, **_kwargs: Any) -> None: # noqa: D401
32
+ return
33
+
34
+
35
+ class Workflow(ABC):
36
+ """Abstract base class for defining a processing workflow."""
37
+
38
+ workflow_schema: ClassVar[WorkflowSchema]
39
+
40
+ def __init__(self) -> None:
41
+ self.validator = WorkflowValidator(self.workflow_schema)
42
+ self.validator.validate()
43
+ self.nodes: Dict[Type[Node], NodeConfig] = self._initialize_nodes()
44
+
45
+ def _observation_context(self, _name: str):
46
+ # Tracing intentionally disabled in vendored version.
47
+ return nullcontext(NoOpSpan())
48
+
49
+ @contextmanager
50
+ def node_context(self, node_name: str):
51
+ logging.info("Starting node: %s", node_name)
52
+ try:
53
+ yield
54
+ except Exception:
55
+ logging.exception("Error in node %s", node_name)
56
+ raise
57
+ finally:
58
+ logging.info("Finished node: %s", node_name)
59
+
60
+ def _initialize_nodes(self) -> Dict[Type[Node], NodeConfig]:
61
+ nodes: Dict[Type[Node], NodeConfig] = {}
62
+ for node_config in self.workflow_schema.nodes:
63
+ nodes[node_config.node] = node_config
64
+ for connected_node in node_config.connections:
65
+ if connected_node not in nodes:
66
+ nodes[connected_node] = NodeConfig(node=connected_node)
67
+ return nodes
68
+
69
+ def run(self, event: Any) -> TaskContext:
70
+ return asyncio.run(self._run(event))
71
+
72
+ async def run_async(self, event: Any) -> TaskContext:
73
+ return await self._run(event)
74
+
75
+ async def run_stream_async(self, event: Any) -> AsyncIterator[Dict[str, Any]]:
76
+ task_context = TaskContext(event=event)
77
+
78
+ with self._observation_context(self.__class__.__name__) as workflow_span:
79
+ try:
80
+ logging.info("Starting workflow streaming execution")
81
+
82
+ task_context.event = self.workflow_schema.event_schema(**event)
83
+ workflow_span.update(input=event)
84
+
85
+ task_context.metadata["nodes"] = self.nodes
86
+ current_node_class: Optional[Type[Node]] = self.workflow_schema.start
87
+
88
+ while current_node_class:
89
+ if task_context.should_stop:
90
+ break
91
+
92
+ current_node = self.nodes[current_node_class].node
93
+ node_name = current_node_class.__name__
94
+
95
+ with self._observation_context(node_name) as node_span:
96
+ node_span.update(
97
+ input=task_context.model_dump(
98
+ exclude={"metadata": {"nodes"}}
99
+ )
100
+ )
101
+
102
+ with self.node_context(node_name):
103
+ if not issubclass(current_node, BaseRouter):
104
+ node_instance = current_node(task_context=task_context)
105
+
106
+ if isinstance(node_instance, AgentStreamingNode):
107
+ async for stream_event in node_instance.process(
108
+ task_context
109
+ ):
110
+ yield stream_event
111
+ else:
112
+ task_context = await node_instance.process(
113
+ task_context
114
+ )
115
+
116
+ node_span.update(
117
+ output=task_context.model_dump(
118
+ include={"nodes": {node_name}}
119
+ )
120
+ )
121
+
122
+ current_node_class = await self._get_next_node_class(
123
+ current_node_class, task_context
124
+ )
125
+
126
+ workflow_span.update(
127
+ output=task_context.model_dump(exclude={"metadata": {"nodes"}})
128
+ )
129
+ task_context.metadata.pop("nodes", None)
130
+
131
+ except Exception as e:
132
+ logging.exception("Error in workflow execution")
133
+ workflow_span.update(level="ERROR", status_message=str(e))
134
+ yield {"type": "error", "error": str(e)}
135
+ raise
136
+
137
+ async def _run(self, event: Any) -> TaskContext:
138
+ task_context = TaskContext(event=event)
139
+
140
+ with self._observation_context(self.__class__.__name__) as workflow_span:
141
+ task_context.event = self.workflow_schema.event_schema(**event)
142
+ workflow_span.update(input=event)
143
+
144
+ task_context.metadata["nodes"] = self.nodes
145
+ current_node_class: Optional[Type[Node]] = self.workflow_schema.start
146
+
147
+ while current_node_class:
148
+ if task_context.should_stop:
149
+ break
150
+
151
+ current_node = self.nodes[current_node_class].node
152
+ node_name = current_node_class.__name__
153
+
154
+ with self._observation_context(node_name) as node_span:
155
+ node_span.update(
156
+ input=task_context.model_dump(exclude={"metadata": {"nodes"}})
157
+ )
158
+
159
+ with self.node_context(node_name):
160
+ if not issubclass(current_node, BaseRouter):
161
+ task_context = await current_node(
162
+ task_context=task_context
163
+ ).process(task_context)
164
+
165
+ node_span.update(
166
+ output=task_context.model_dump(include={"nodes": {node_name}})
167
+ )
168
+
169
+ current_node_class = await self._get_next_node_class(
170
+ current_node_class, task_context
171
+ )
172
+
173
+ workflow_span.update(
174
+ output=task_context.model_dump(exclude={"metadata": {"nodes"}})
175
+ )
176
+ task_context.metadata.pop("nodes", None)
177
+ return task_context
178
+
179
+ async def _get_next_node_class(
180
+ self, current_node_class: Type[Node], task_context: TaskContext
181
+ ) -> Optional[Type[Node]]:
182
+ node_config = next(
183
+ (nc for nc in self.workflow_schema.nodes if nc.node == current_node_class),
184
+ None,
185
+ )
186
+
187
+ if not node_config or not node_config.connections:
188
+ return None
189
+
190
+ if node_config.is_router:
191
+ router: BaseRouter = self.nodes[current_node_class].node() # type: ignore[assignment]
192
+ return await self._handle_router(router, task_context)
193
+
194
+ return node_config.connections[0]
195
+
196
+ async def _handle_router(
197
+ self, router: BaseRouter, task_context: TaskContext
198
+ ) -> Optional[Type[Node]]:
199
+ next_node = router.route(task_context)
200
+ return next_node.__class__ if next_node else None