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.
- devflow_engine/__init__.py +3 -0
- devflow_engine/agentic_prompts.py +100 -0
- devflow_engine/agentic_runtime.py +398 -0
- devflow_engine/api_key_flow_harness.py +539 -0
- devflow_engine/api_keys.py +357 -0
- devflow_engine/bootstrap/__init__.py +2 -0
- devflow_engine/bootstrap/provision_from_template.py +84 -0
- devflow_engine/cli/__init__.py +0 -0
- devflow_engine/cli/app.py +7270 -0
- devflow_engine/core/__init__.py +0 -0
- devflow_engine/core/config.py +86 -0
- devflow_engine/core/logging.py +29 -0
- devflow_engine/core/paths.py +45 -0
- devflow_engine/core/toml_kv.py +33 -0
- devflow_engine/devflow_event_worker.py +1292 -0
- devflow_engine/devflow_state.py +201 -0
- devflow_engine/devin2/__init__.py +9 -0
- devflow_engine/devin2/agent_definition.py +120 -0
- devflow_engine/devin2/pi_runner.py +204 -0
- devflow_engine/devin_orchestration.py +69 -0
- devflow_engine/docs/prompts/anti-patterns.md +42 -0
- devflow_engine/docs/prompts/devin-agent-prompt.md +55 -0
- devflow_engine/docs/prompts/devin2-agent-prompt.md +81 -0
- devflow_engine/docs/prompts/examples/devin-vapi-clone-reference-exchange.json +85 -0
- devflow_engine/doctor/__init__.py +2 -0
- devflow_engine/doctor/triage.py +140 -0
- devflow_engine/error/__init__.py +0 -0
- devflow_engine/error/remediation.py +21 -0
- devflow_engine/errors/error_solver_dag.py +522 -0
- devflow_engine/errors/runtime_observability.py +67 -0
- devflow_engine/idea/__init__.py +4 -0
- devflow_engine/idea/actors.py +481 -0
- devflow_engine/idea/agentic.py +465 -0
- devflow_engine/idea/analyze.py +93 -0
- devflow_engine/idea/devin_chat_dag.py +1 -0
- devflow_engine/idea/diff.py +99 -0
- devflow_engine/idea/drafts.py +446 -0
- devflow_engine/idea/idea_creation_dag.py +643 -0
- devflow_engine/idea/ideation_enrichment.py +355 -0
- devflow_engine/idea/ideation_enrichment_worker.py +19 -0
- devflow_engine/idea/paths.py +28 -0
- devflow_engine/idea/promote.py +53 -0
- devflow_engine/idea/redaction.py +27 -0
- devflow_engine/idea/repo_tools.py +1277 -0
- devflow_engine/idea/response_mode.py +30 -0
- devflow_engine/idea/story_pipeline.py +1585 -0
- devflow_engine/idea/sufficiency.py +376 -0
- devflow_engine/idea/traditional_stories.py +1257 -0
- devflow_engine/implementation/__init__.py +0 -0
- devflow_engine/implementation/alembic_preflight.py +700 -0
- devflow_engine/implementation/dag.py +8450 -0
- devflow_engine/implementation/green_gate.py +93 -0
- devflow_engine/implementation/prompts.py +108 -0
- devflow_engine/implementation/test_runtime.py +623 -0
- devflow_engine/integration/__init__.py +19 -0
- devflow_engine/integration/agentic.py +66 -0
- devflow_engine/integration/dag.py +3539 -0
- devflow_engine/integration/prompts.py +114 -0
- devflow_engine/integration/supabase_schema.sql +31 -0
- devflow_engine/integration/supabase_sync.py +177 -0
- devflow_engine/llm/__init__.py +1 -0
- devflow_engine/llm/cli_one_shot.py +84 -0
- devflow_engine/llm/cli_stream.py +371 -0
- devflow_engine/llm/execution_context.py +26 -0
- devflow_engine/llm/invoke.py +1322 -0
- devflow_engine/llm/provider_api.py +304 -0
- devflow_engine/llm/repo_knowledge.py +588 -0
- devflow_engine/llm_primitives.py +315 -0
- devflow_engine/orchestration.py +62 -0
- devflow_engine/planning/__init__.py +0 -0
- devflow_engine/planning/analyze_repo.py +92 -0
- devflow_engine/planning/render_drafts.py +133 -0
- devflow_engine/playground/__init__.py +0 -0
- devflow_engine/playground/hooks.py +26 -0
- devflow_engine/playwright_workflow/__init__.py +5 -0
- devflow_engine/playwright_workflow/dag.py +1317 -0
- devflow_engine/process/__init__.py +5 -0
- devflow_engine/process/dag.py +59 -0
- devflow_engine/project_registration/__init__.py +3 -0
- devflow_engine/project_registration/dag.py +1581 -0
- devflow_engine/project_registry.py +109 -0
- devflow_engine/prompts/devin/generic/prompt.md +6 -0
- devflow_engine/prompts/devin/ideation/prompt.md +263 -0
- devflow_engine/prompts/devin/ideation/scenarios.md +5 -0
- devflow_engine/prompts/devin/ideation_loop/prompt.md +6 -0
- devflow_engine/prompts/devin/insight/prompt.md +11 -0
- devflow_engine/prompts/devin/insight/scenarios.md +5 -0
- devflow_engine/prompts/devin/intake/prompt.md +15 -0
- devflow_engine/prompts/devin/iterate/prompt.md +12 -0
- devflow_engine/prompts/devin/shared/eval_doctrine.md +9 -0
- devflow_engine/prompts/devin/shared/principles.md +246 -0
- devflow_engine/prompts/devin_eval/assessment/prompt.md +18 -0
- devflow_engine/prompts/idea/api_ideation_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/api_insight_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/response_doctrine/prompt.md +18 -0
- devflow_engine/prompts/implementation/dependency_assessment/prompt.md +12 -0
- devflow_engine/prompts/implementation/green/green/prompt.md +11 -0
- devflow_engine/prompts/implementation/green/node_config/prompt.md +3 -0
- devflow_engine/prompts/implementation/green_review/outcome_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/green_review/prior_run_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/red/prompt.md +27 -0
- devflow_engine/prompts/implementation/redreview/prompt.md +23 -0
- devflow_engine/prompts/implementation/redreview_repair/prompt.md +16 -0
- devflow_engine/prompts/implementation/setupdoc/prompt.md +10 -0
- devflow_engine/prompts/implementation/story_planning/prompt.md +13 -0
- devflow_engine/prompts/implementation/test_design/prompt.md +27 -0
- devflow_engine/prompts/integration/README.md +185 -0
- devflow_engine/prompts/integration/green/example.md +67 -0
- devflow_engine/prompts/integration/green/green/prompt.md +10 -0
- devflow_engine/prompts/integration/green/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/green/prompt.md +15 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green_enrich/example.md +79 -0
- devflow_engine/prompts/integration/green_enrich/green_enrich/prompt.md +9 -0
- devflow_engine/prompts/integration/green_enrich/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/green_enrich/prompt.md +14 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red/code_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red/example.md +152 -0
- devflow_engine/prompts/integration/red/node_config/prompt.md +86 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red/prompt.md +16 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red_repair/prompt.md +15 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/red_repair/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red_review/example.md +71 -0
- devflow_engine/prompts/integration/red_review/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/red_review/prompt.md +15 -0
- devflow_engine/prompts/integration/red_review/red_review/prompt.md +9 -0
- devflow_engine/prompts/integration/resolve/example.md +111 -0
- devflow_engine/prompts/integration/resolve/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_implicated_users/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_side_effects/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/resolve_implicated_users/prompt.md +10 -0
- devflow_engine/prompts/integration/resolve/resolve_side_effects/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/build_idea_acceptance_coverage/prompt.md +12 -0
- devflow_engine/prompts/integration/validate/code_repair/prompt.md +13 -0
- devflow_engine/prompts/integration/validate/example.md +143 -0
- devflow_engine/prompts/integration/validate/node_config/prompt.md +87 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_enrich_gate/prompt.md +17 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_repair/prompt.md +16 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/validate_repair/prompt.md +9 -0
- devflow_engine/prompts/integration/validate/validate_enrich_gate/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/validate_repair/prompt.md +20 -0
- devflow_engine/prompts/integration/write_workflows/example.md +100 -0
- devflow_engine/prompts/integration/write_workflows/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/write_workflows/prompt.md +17 -0
- devflow_engine/prompts/integration/write_workflows/write_workflows/prompt.md +11 -0
- devflow_engine/prompts/iterate/README.md +7 -0
- devflow_engine/prompts/iterate/coder/prompt.md +11 -0
- devflow_engine/prompts/iterate/framer/prompt.md +11 -0
- devflow_engine/prompts/iterate/iterator/prompt.md +13 -0
- devflow_engine/prompts/iterate/observer/prompt.md +11 -0
- devflow_engine/prompts/recovery/diagnosis/prompt.md +7 -0
- devflow_engine/prompts/recovery/execution/prompt.md +8 -0
- devflow_engine/prompts/recovery/execution_verification/prompt.md +7 -0
- devflow_engine/prompts/recovery/failure_investigation/prompt.md +10 -0
- devflow_engine/prompts/recovery/preflight_health_repo_repair/prompt.md +8 -0
- devflow_engine/prompts/recovery/remediation_execution/prompt.md +11 -0
- devflow_engine/prompts/recovery/root_cause_investigation/prompt.md +12 -0
- devflow_engine/prompts/scope_idea/doctrine/prompt.md +7 -0
- devflow_engine/prompts/source_doc_eval/document/prompt.md +6 -0
- devflow_engine/prompts/source_doc_eval/targeted_mutation/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/domain_entities/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/product_brief/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_coherence/prompt.md +7 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_render/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_coherence/prompt.md +5 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_enrichment_coherence/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/user_workflows/prompt.md +6 -0
- devflow_engine/prompts/source_scope/doctrine/prompt.md +10 -0
- devflow_engine/prompts/ui_grounding/doctrine/prompt.md +7 -0
- devflow_engine/recovery/__init__.py +3 -0
- devflow_engine/recovery/dag.py +2609 -0
- devflow_engine/recovery/models.py +220 -0
- devflow_engine/refactor.py +93 -0
- devflow_engine/registry/__init__.py +1 -0
- devflow_engine/registry/cards.py +238 -0
- devflow_engine/registry/domain_normalize.py +60 -0
- devflow_engine/registry/effects.py +65 -0
- devflow_engine/registry/enforce_report.py +150 -0
- devflow_engine/registry/module_cards_classify.py +164 -0
- devflow_engine/registry/module_cards_draft.py +184 -0
- devflow_engine/registry/module_cards_gate.py +59 -0
- devflow_engine/registry/packages.py +347 -0
- devflow_engine/registry/pathways.py +323 -0
- devflow_engine/review/__init__.py +11 -0
- devflow_engine/review/dag.py +588 -0
- devflow_engine/review/review_story.py +67 -0
- devflow_engine/scope_idea/__init__.py +3 -0
- devflow_engine/scope_idea/agentic.py +39 -0
- devflow_engine/scope_idea/dag.py +1069 -0
- devflow_engine/scope_idea/models.py +175 -0
- devflow_engine/skills/builtins/devflow/queue_failure_investigation/SKILL.md +112 -0
- devflow_engine/skills/builtins/devflow/queue_idea_to_story/SKILL.md +120 -0
- devflow_engine/skills/builtins/devflow/queue_integration/SKILL.md +105 -0
- devflow_engine/skills/builtins/devflow/queue_recovery/SKILL.md +108 -0
- devflow_engine/skills/builtins/devflow/queue_runtime_core/SKILL.md +155 -0
- devflow_engine/skills/builtins/devflow/queue_story_implementation/SKILL.md +122 -0
- devflow_engine/skills/builtins/devin/idea_to_story_handoff/SKILL.md +120 -0
- devflow_engine/skills/builtins/devin/ideation/SKILL.md +168 -0
- devflow_engine/skills/builtins/devin/ideation/state-and-phrasing-reference.md +18 -0
- devflow_engine/skills/builtins/devin/insight/SKILL.md +22 -0
- devflow_engine/skills/registry.example.yaml +42 -0
- devflow_engine/source_doc_assumptions.py +291 -0
- devflow_engine/source_doc_mutation_dag.py +1606 -0
- devflow_engine/source_doc_mutation_eval.py +417 -0
- devflow_engine/source_doc_mutation_worker.py +25 -0
- devflow_engine/source_docs_schema.py +207 -0
- devflow_engine/source_docs_updater.py +309 -0
- devflow_engine/source_scope/__init__.py +15 -0
- devflow_engine/source_scope/agentic.py +45 -0
- devflow_engine/source_scope/dag.py +1626 -0
- devflow_engine/source_scope/models.py +177 -0
- devflow_engine/stores/__init__.py +0 -0
- devflow_engine/stores/execution_store.py +3534 -0
- devflow_engine/story/__init__.py +0 -0
- devflow_engine/story/contracts.py +160 -0
- devflow_engine/story/discovery.py +47 -0
- devflow_engine/story/evidence.py +118 -0
- devflow_engine/story/hashing.py +27 -0
- devflow_engine/story/implemented_queue_purge.py +148 -0
- devflow_engine/story/indexer.py +105 -0
- devflow_engine/story/io.py +20 -0
- devflow_engine/story/markdown_contracts.py +298 -0
- devflow_engine/story/reconciliation.py +408 -0
- devflow_engine/story/validate_stories.py +149 -0
- devflow_engine/story/validate_tests_story.py +512 -0
- devflow_engine/story/validation.py +133 -0
- devflow_engine/ui_grounding/__init__.py +11 -0
- devflow_engine/ui_grounding/agentic.py +31 -0
- devflow_engine/ui_grounding/dag.py +874 -0
- devflow_engine/ui_grounding/models.py +224 -0
- devflow_engine/ui_grounding/pencil_bridge.py +247 -0
- devflow_engine/vendor/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/__init__.py +11 -0
- devflow_engine/vendor/datalumina_genai/core/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/exceptions.py +9 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent.py +48 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent_streaming_node.py +26 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/base.py +89 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/concurrent.py +30 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/router.py +69 -0
- devflow_engine/vendor/datalumina_genai/core/schema.py +72 -0
- devflow_engine/vendor/datalumina_genai/core/task.py +52 -0
- devflow_engine/vendor/datalumina_genai/core/validate.py +139 -0
- devflow_engine/vendor/datalumina_genai/core/workflow.py +200 -0
- devflow_engine/worker.py +1086 -0
- devflow_engine/worker_guard.py +233 -0
- devflow_engine-1.0.0.dist-info/METADATA +235 -0
- devflow_engine-1.0.0.dist-info/RECORD +393 -0
- devflow_engine-1.0.0.dist-info/WHEEL +4 -0
- devflow_engine-1.0.0.dist-info/entry_points.txt +3 -0
- devin/__init__.py +6 -0
- devin/dag.py +58 -0
- devin/dag_two_arm.py +138 -0
- devin/devin_chat_scenario_catalog.json +588 -0
- devin/devin_eval.py +677 -0
- devin/nodes/__init__.py +0 -0
- devin/nodes/ideation/__init__.py +0 -0
- devin/nodes/ideation/node.py +195 -0
- devin/nodes/ideation/playground.py +267 -0
- devin/nodes/ideation/prompt.md +65 -0
- devin/nodes/ideation/scenarios/continue_refinement.py +13 -0
- devin/nodes/ideation/scenarios/continue_refinement_evals.py +18 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns.py +17 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns_evals.py +16 -0
- devin/nodes/ideation/scenarios/large_idea_split.py +4 -0
- devin/nodes/ideation/scenarios/large_idea_split_evals.py +17 -0
- devin/nodes/ideation/scenarios/source_documentation_added.py +4 -0
- devin/nodes/ideation/scenarios/source_documentation_added_evals.py +16 -0
- devin/nodes/ideation/scenarios/user_says_create_it.py +30 -0
- devin/nodes/ideation/scenarios/user_says_create_it_evals.py +23 -0
- devin/nodes/ideation/scenarios/vague_idea.py +16 -0
- devin/nodes/ideation/scenarios/vague_idea_evals.py +47 -0
- devin/nodes/ideation/tools.json +312 -0
- devin/nodes/insight/__init__.py +0 -0
- devin/nodes/insight/node.py +49 -0
- devin/nodes/insight/playground.py +154 -0
- devin/nodes/insight/prompt.md +61 -0
- devin/nodes/insight/scenarios/architecture_pattern_query.py +15 -0
- devin/nodes/insight/scenarios/architecture_pattern_query_evals.py +25 -0
- devin/nodes/insight/scenarios/codebase_exploration.py +15 -0
- devin/nodes/insight/scenarios/codebase_exploration_evals.py +23 -0
- devin/nodes/insight/scenarios/devin_ideation_routing.py +19 -0
- devin/nodes/insight/scenarios/devin_ideation_routing_evals.py +39 -0
- devin/nodes/insight/scenarios/devin_insight_routing.py +20 -0
- devin/nodes/insight/scenarios/devin_insight_routing_evals.py +40 -0
- devin/nodes/insight/scenarios/operational_debugging.py +15 -0
- devin/nodes/insight/scenarios/operational_debugging_evals.py +23 -0
- devin/nodes/insight/scenarios/operational_question.py +9 -0
- devin/nodes/insight/scenarios/operational_question_evals.py +8 -0
- devin/nodes/insight/scenarios/queue_status.py +15 -0
- devin/nodes/insight/scenarios/queue_status_evals.py +23 -0
- devin/nodes/insight/scenarios/source_doc_explanation.py +14 -0
- devin/nodes/insight/scenarios/source_doc_explanation_evals.py +21 -0
- devin/nodes/insight/scenarios/worker_state_check.py +15 -0
- devin/nodes/insight/scenarios/worker_state_check_evals.py +22 -0
- devin/nodes/insight/tools.json +126 -0
- devin/nodes/intake/__init__.py +0 -0
- devin/nodes/intake/node.py +27 -0
- devin/nodes/intake/playground.py +47 -0
- devin/nodes/intake/prompt.md +12 -0
- devin/nodes/intake/scenarios/ideation_routing.py +4 -0
- devin/nodes/intake/scenarios/ideation_routing_evals.py +5 -0
- devin/nodes/intake/scenarios/insight_routing.py +4 -0
- devin/nodes/intake/scenarios/insight_routing_evals.py +5 -0
- devin/nodes/iterate/README.md +44 -0
- devin/nodes/iterate/__init__.py +1 -0
- devin/nodes/iterate/_archived_design_stages/01-objectives-requirements.md +112 -0
- devin/nodes/iterate/_archived_design_stages/02-evals.md +131 -0
- devin/nodes/iterate/_archived_design_stages/03-tools-and-boundaries.md +110 -0
- devin/nodes/iterate/_archived_design_stages/04-harness-and-playground.md +32 -0
- devin/nodes/iterate/_archived_design_stages/05-prompt-deferred.md +11 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/03-tools-and-boundaries.md +13 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/01-objectives-requirements.md +25 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/02-evals.md +9 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/04-harness-and-playground.md +13 -0
- devin/nodes/iterate/agent-roles.md +89 -0
- devin/nodes/iterate/agents/README.md +10 -0
- devin/nodes/iterate/artifacts.md +504 -0
- devin/nodes/iterate/contract.md +100 -0
- devin/nodes/iterate/eval-plan.md +74 -0
- devin/nodes/iterate/node.py +100 -0
- devin/nodes/iterate/pipeline/README.md +13 -0
- devin/nodes/iterate/playground-contract.md +76 -0
- devin/nodes/iterate/prompt.md +11 -0
- devin/nodes/iterate/scenarios/README.md +38 -0
- devin/nodes/iterate/scenarios/artifact-and-loop-scenarios.md +101 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment.py +32 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment_evals.py +45 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix.py +27 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix_evals.py +45 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing.py +21 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing_evals.py +36 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary.py +25 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary_evals.py +57 -0
- devin/nodes/iterate/scenarios/framer_task_framing.py +25 -0
- devin/nodes/iterate/scenarios/framer_task_framing_evals.py +58 -0
- devin/nodes/iterate/scenarios/iterate_error_fix.py +21 -0
- devin/nodes/iterate/scenarios/iterate_error_fix_evals.py +39 -0
- devin/nodes/iterate/scenarios/iterate_quick_change.py +21 -0
- devin/nodes/iterate/scenarios/iterate_quick_change_evals.py +35 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion_evals.py +53 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute_evals.py +53 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam.py +28 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam_evals.py +55 -0
- devin/nodes/iterate/scenarios/observer_repro_creation.py +28 -0
- devin/nodes/iterate/scenarios/observer_repro_creation_evals.py +45 -0
- devin/nodes/iterate/scenarios/routing-matrix.md +45 -0
- devin/nodes/shared/__init__.py +0 -0
- devin/nodes/shared/filemaker_expert.md +80 -0
- devin/nodes/shared/filemaker_expert.py +354 -0
- devin/nodes/shared/filemaker_expert_eval/runner.py +176 -0
- devin/nodes/shared/filemaker_expert_eval/scenarios.json +65 -0
- devin/nodes/shared/goldilocks_advisor_eval/runner.py +214 -0
- devin/nodes/shared/goldilocks_advisor_eval/scenarios.json +58 -0
- devin/nodes/shared/helpers.py +156 -0
- devin/nodes/shared/idea_compliance_advisor_eval/runner.py +252 -0
- devin/nodes/shared/idea_compliance_advisor_eval/scenarios.json +75 -0
- devin/nodes/shared/models.py +44 -0
- devin/nodes/shared/post.py +40 -0
- devin/nodes/shared/router.py +107 -0
- devin/nodes/shared/tools.py +191 -0
- devin/shared/devin-chat-rubric.md +237 -0
- devin/shared/devin-chat-scenario-suite.md +90 -0
- devin/shared/eval_doctrine.md +9 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _keychain_get(service: str, account: str) -> str | None:
|
|
12
|
+
try:
|
|
13
|
+
proc = subprocess.run(
|
|
14
|
+
["security", "find-generic-password", "-s", service, "-a", account, "-w"],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=False,
|
|
18
|
+
timeout=10,
|
|
19
|
+
)
|
|
20
|
+
except Exception:
|
|
21
|
+
return None
|
|
22
|
+
if proc.returncode != 0:
|
|
23
|
+
return None
|
|
24
|
+
value = proc.stdout.strip()
|
|
25
|
+
return value or None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _resolve_supabase_rest_config() -> tuple[str, str] | None:
|
|
29
|
+
if os.environ.get("PYTEST_CURRENT_TEST"):
|
|
30
|
+
return None
|
|
31
|
+
url = (
|
|
32
|
+
os.environ.get("DEVFLOW_SUPABASE_URL")
|
|
33
|
+
or os.environ.get("SUPABASE_URL")
|
|
34
|
+
or _keychain_get("Supabase URL", "Clarity")
|
|
35
|
+
)
|
|
36
|
+
key = (
|
|
37
|
+
os.environ.get("DEVFLOW_SUPABASE_SERVICE_KEY")
|
|
38
|
+
or os.environ.get("SUPABASE_SERVICE_ROLE_KEY")
|
|
39
|
+
or os.environ.get("SUPABASE_SERVICE_KEY")
|
|
40
|
+
or _keychain_get("Supabase Service Key", "Clarity")
|
|
41
|
+
)
|
|
42
|
+
if not url or not key:
|
|
43
|
+
return None
|
|
44
|
+
return url.rstrip("/"), key
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _postgrest_request(*, method: str, url: str, key: str, body: Any | None = None, prefer: str | None = None) -> Any:
|
|
48
|
+
payload = None if body is None else json.dumps(body).encode("utf-8")
|
|
49
|
+
req = Request(url, data=payload, method=method)
|
|
50
|
+
req.add_header("apikey", key)
|
|
51
|
+
req.add_header("Authorization", f"Bearer {key}")
|
|
52
|
+
if body is not None:
|
|
53
|
+
req.add_header("Content-Type", "application/json")
|
|
54
|
+
if prefer:
|
|
55
|
+
req.add_header("Prefer", prefer)
|
|
56
|
+
with urlopen(req, timeout=30) as resp:
|
|
57
|
+
raw = resp.read().decode("utf-8")
|
|
58
|
+
return json.loads(raw) if raw else None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _is_uuid_like(value: str | None) -> bool:
|
|
62
|
+
if not value:
|
|
63
|
+
return False
|
|
64
|
+
import re
|
|
65
|
+
|
|
66
|
+
pattern = r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
|
67
|
+
return bool(re.fullmatch(pattern, value.strip()))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _resolve_dfs_project_id(*, url: str, key: str, project_id: str, display_path: str | None) -> str:
|
|
71
|
+
value = str(project_id or '').strip()
|
|
72
|
+
if _is_uuid_like(value):
|
|
73
|
+
return value
|
|
74
|
+
display_value = str(display_path or '').strip()
|
|
75
|
+
idea_id = None
|
|
76
|
+
if display_value.startswith('idea:'):
|
|
77
|
+
candidate = display_value.split(':', 1)[1].strip()
|
|
78
|
+
if candidate.startswith('idea_'):
|
|
79
|
+
idea_id = candidate
|
|
80
|
+
elif display_value.startswith('story:'):
|
|
81
|
+
candidate = display_value.split(':', 1)[1].strip()
|
|
82
|
+
if candidate.startswith('STORY:idea:'):
|
|
83
|
+
parts = candidate.split(':')
|
|
84
|
+
if len(parts) >= 3 and parts[2].startswith('idea_'):
|
|
85
|
+
idea_id = parts[2]
|
|
86
|
+
if not idea_id:
|
|
87
|
+
return value
|
|
88
|
+
try:
|
|
89
|
+
lookup_url = f"{url}/rest/v1/devflow_project_ideas?select=project_id&idea_id=eq.{idea_id}&limit=1"
|
|
90
|
+
rows = _postgrest_request(method='GET', url=lookup_url, key=key)
|
|
91
|
+
except Exception:
|
|
92
|
+
return value
|
|
93
|
+
if isinstance(rows, list) and rows:
|
|
94
|
+
resolved = str((rows[0] or {}).get('project_id') or '').strip()
|
|
95
|
+
if resolved:
|
|
96
|
+
return resolved
|
|
97
|
+
return value
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def publish_devflow_state(
|
|
101
|
+
*,
|
|
102
|
+
project_id: str,
|
|
103
|
+
run_id: str | None,
|
|
104
|
+
current_state: str,
|
|
105
|
+
current_status: str,
|
|
106
|
+
run_summary: str,
|
|
107
|
+
error_message: str | None = None,
|
|
108
|
+
display: str = "project",
|
|
109
|
+
display_path: str | None = None,
|
|
110
|
+
) -> None:
|
|
111
|
+
config = _resolve_supabase_rest_config()
|
|
112
|
+
if config is None:
|
|
113
|
+
return
|
|
114
|
+
url, key = config
|
|
115
|
+
resolved_project_id = _resolve_dfs_project_id(url=url, key=key, project_id=project_id, display_path=display_path)
|
|
116
|
+
row = {
|
|
117
|
+
"project_id": resolved_project_id,
|
|
118
|
+
"run_id": run_id,
|
|
119
|
+
"current_state": current_state,
|
|
120
|
+
"current_status": current_status,
|
|
121
|
+
"error_code": None,
|
|
122
|
+
"error_message": error_message,
|
|
123
|
+
"display": display,
|
|
124
|
+
"display_path": display_path,
|
|
125
|
+
"run_summary": run_summary,
|
|
126
|
+
"updated_at": datetime.now(UTC).isoformat(),
|
|
127
|
+
}
|
|
128
|
+
try:
|
|
129
|
+
_postgrest_request(
|
|
130
|
+
method="POST",
|
|
131
|
+
url=f"{url}/rest/v1/devflow_state?on_conflict=project_id",
|
|
132
|
+
key=key,
|
|
133
|
+
body=row,
|
|
134
|
+
prefer="resolution=merge-duplicates",
|
|
135
|
+
)
|
|
136
|
+
except Exception:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def fetch_story_statuses_from_supabase(*, story_ids: list[str]) -> dict[str, str]:
|
|
141
|
+
"""Return Supabase story statuses keyed by story_id for the requested story ids."""
|
|
142
|
+
config = _resolve_supabase_rest_config()
|
|
143
|
+
if config is None:
|
|
144
|
+
return {}
|
|
145
|
+
normalized = [str(story_id or "").strip() for story_id in story_ids]
|
|
146
|
+
wanted = sorted({story_id for story_id in normalized if story_id})
|
|
147
|
+
if not wanted:
|
|
148
|
+
return {}
|
|
149
|
+
|
|
150
|
+
url, key = config
|
|
151
|
+
rows_by_story_id: dict[str, str] = {}
|
|
152
|
+
try:
|
|
153
|
+
from urllib.parse import quote
|
|
154
|
+
|
|
155
|
+
batch_size = 100
|
|
156
|
+
for offset in range(0, len(wanted), batch_size):
|
|
157
|
+
batch = wanted[offset : offset + batch_size]
|
|
158
|
+
encoded_values = ",".join(quote(f'"{story_id}"', safe="") for story_id in batch)
|
|
159
|
+
rows = _postgrest_request(
|
|
160
|
+
method="GET",
|
|
161
|
+
url=(
|
|
162
|
+
f"{url}/rest/v1/devflow_idea_stories?select=story_id,status"
|
|
163
|
+
f"&story_id=in.({encoded_values})"
|
|
164
|
+
),
|
|
165
|
+
key=key,
|
|
166
|
+
)
|
|
167
|
+
if not isinstance(rows, list):
|
|
168
|
+
continue
|
|
169
|
+
for row in rows:
|
|
170
|
+
if not isinstance(row, dict):
|
|
171
|
+
continue
|
|
172
|
+
story_id = str(row.get("story_id") or "").strip()
|
|
173
|
+
status = str(row.get("status") or "").strip()
|
|
174
|
+
if story_id:
|
|
175
|
+
rows_by_story_id[story_id] = status
|
|
176
|
+
except Exception:
|
|
177
|
+
return {}
|
|
178
|
+
return rows_by_story_id
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def update_story_status_in_supabase(*, story_id: str, status: str) -> None:
|
|
182
|
+
"""Mirror story implementation status to Supabase devflow_idea_stories."""
|
|
183
|
+
config = _resolve_supabase_rest_config()
|
|
184
|
+
if config is None:
|
|
185
|
+
return
|
|
186
|
+
url, key = config
|
|
187
|
+
try:
|
|
188
|
+
from urllib.parse import quote
|
|
189
|
+
|
|
190
|
+
row = {
|
|
191
|
+
"status": status,
|
|
192
|
+
"updated_at": datetime.now(UTC).isoformat(),
|
|
193
|
+
}
|
|
194
|
+
_postgrest_request(
|
|
195
|
+
method="PATCH",
|
|
196
|
+
url=f"{url}/rest/v1/devflow_idea_stories?story_id=eq.{quote(story_id)}",
|
|
197
|
+
key=key,
|
|
198
|
+
body=row,
|
|
199
|
+
)
|
|
200
|
+
except Exception:
|
|
201
|
+
return # non-fatal — local state is source of truth
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.resources import files
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
10
|
+
_DEVIN2_PROMPT_PATH = _REPO_ROOT / "docs" / "prompts" / "devin2-agent-prompt.md"
|
|
11
|
+
_PROJECT_JOURNAL_RELATIVE_DIR = ".devflow/journal"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Devin2AgentPromptPayload(BaseModel):
|
|
15
|
+
agent_name: str
|
|
16
|
+
route_arm: Literal["ideation", "insight", "iterate", "neither"]
|
|
17
|
+
prompt_path: str
|
|
18
|
+
prompt_markdown: str
|
|
19
|
+
project_journal: dict[str, Any]
|
|
20
|
+
separation_contract: dict[str, Any]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def devin2_agent_prompt_path() -> Path:
|
|
24
|
+
if _DEVIN2_PROMPT_PATH.exists():
|
|
25
|
+
return _DEVIN2_PROMPT_PATH
|
|
26
|
+
return Path(str(files("devflow_engine").joinpath("docs", "prompts", "devin2-agent-prompt.md")))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def load_devin2_agent_prompt() -> str:
|
|
30
|
+
return devin2_agent_prompt_path().read_text(encoding="utf-8").strip()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_devin2_agent_prompt_payload(
|
|
34
|
+
*,
|
|
35
|
+
repo_root: Path,
|
|
36
|
+
route_arm: Literal["ideation", "insight", "iterate"],
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
journal_root = repo_root / _PROJECT_JOURNAL_RELATIVE_DIR
|
|
39
|
+
return Devin2AgentPromptPayload(
|
|
40
|
+
agent_name="Devin 2.0",
|
|
41
|
+
route_arm=route_arm,
|
|
42
|
+
prompt_path=str(devin2_agent_prompt_path()),
|
|
43
|
+
prompt_markdown=load_devin2_agent_prompt(),
|
|
44
|
+
project_journal={
|
|
45
|
+
"relative_dir": _PROJECT_JOURNAL_RELATIVE_DIR,
|
|
46
|
+
"absolute_dir": str(journal_root),
|
|
47
|
+
"entry_pattern": ".devflow/journal/YYYY-MM-DD.md",
|
|
48
|
+
"write_rule": (
|
|
49
|
+
"Persist material assumptions, decisions, and continuity notes here "
|
|
50
|
+
"when they matter across turns or later execution."
|
|
51
|
+
),
|
|
52
|
+
},
|
|
53
|
+
separation_contract={
|
|
54
|
+
"baseline_path": "docs/prompts/devin-agent-prompt.md",
|
|
55
|
+
"v2_path": "docs/prompts/devin2-agent-prompt.md",
|
|
56
|
+
"prompt_role": "Defines Devin 2.0's conversational stance and ownership split.",
|
|
57
|
+
"operational_guidance_role": (
|
|
58
|
+
"Supplies turn-specific workflow rules, route constraints, and execution caveats."
|
|
59
|
+
),
|
|
60
|
+
"runtime_contract_role": "Defines the JSON output schema and return contract only.",
|
|
61
|
+
},
|
|
62
|
+
).model_dump()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def build_inline_agent_prompt_payload(
|
|
66
|
+
*,
|
|
67
|
+
prompt_text: str,
|
|
68
|
+
route_arm: Literal["ideation", "insight", "iterate", "neither"],
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
return {
|
|
71
|
+
"agent_name": "inline_response_agent",
|
|
72
|
+
"route_arm": route_arm,
|
|
73
|
+
"prompt_text": str(prompt_text).strip(),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def build_response_prompt_payload(
|
|
78
|
+
*,
|
|
79
|
+
stage_name: str,
|
|
80
|
+
context_payload: dict[str, Any],
|
|
81
|
+
operational_guidance: list[str],
|
|
82
|
+
output_model: type[BaseModel],
|
|
83
|
+
agent_prompt: dict[str, Any],
|
|
84
|
+
) -> dict[str, Any]:
|
|
85
|
+
return {
|
|
86
|
+
"task": stage_name,
|
|
87
|
+
"agent_prompt": agent_prompt,
|
|
88
|
+
"operational_guidance": list(operational_guidance),
|
|
89
|
+
"context": context_payload,
|
|
90
|
+
"runtime_contract": {
|
|
91
|
+
"output_schema": output_model.model_json_schema(),
|
|
92
|
+
"return_format": "json_only",
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def build_artifact_reference_response_prompt_payload(
|
|
98
|
+
*,
|
|
99
|
+
stage_name: str,
|
|
100
|
+
operational_guidance: list[str],
|
|
101
|
+
output_model: type[BaseModel],
|
|
102
|
+
agent_prompt: dict[str, Any],
|
|
103
|
+
artifact_path: Path,
|
|
104
|
+
) -> dict[str, Any]:
|
|
105
|
+
return {
|
|
106
|
+
"task": stage_name,
|
|
107
|
+
"agent_prompt": agent_prompt,
|
|
108
|
+
"operational_guidance": list(operational_guidance)
|
|
109
|
+
+ [
|
|
110
|
+
"The full context was persisted to disk because the prompt exceeded size limits.",
|
|
111
|
+
f"Read context from artifact_path={artifact_path}",
|
|
112
|
+
],
|
|
113
|
+
"context_artifact": {
|
|
114
|
+
"artifact_path": str(artifact_path),
|
|
115
|
+
},
|
|
116
|
+
"runtime_contract": {
|
|
117
|
+
"output_schema": output_model.model_json_schema(),
|
|
118
|
+
"return_format": "json_only",
|
|
119
|
+
},
|
|
120
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ValidationError
|
|
10
|
+
|
|
11
|
+
from ..idea.traditional_stories import _extract_json
|
|
12
|
+
from ..llm.invoke import (
|
|
13
|
+
LlmInvocationRequest,
|
|
14
|
+
LlmInvocationResult,
|
|
15
|
+
invoke_llm,
|
|
16
|
+
resolve_api_tier_request_overrides,
|
|
17
|
+
resolve_canonical_light_tier_model,
|
|
18
|
+
)
|
|
19
|
+
from .agent_definition import (
|
|
20
|
+
build_artifact_reference_response_prompt_payload,
|
|
21
|
+
build_devin2_agent_prompt_payload,
|
|
22
|
+
build_response_prompt_payload,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_PI_PROVIDER = "pi"
|
|
26
|
+
_PI_BASE_CMD = "pi"
|
|
27
|
+
_PI_DELIVERY = "argument"
|
|
28
|
+
_PI_DEVFLOW_TOOLS_EXT = str(Path.home() / ".pi/agent/extensions/devflow-tools.ts")
|
|
29
|
+
_PI_BASE_CMD_WITH_DEVFLOW_TOOLS = f"pi -e {_PI_DEVFLOW_TOOLS_EXT}"
|
|
30
|
+
_MAX_PROMPT_CHARS = 120_000
|
|
31
|
+
_PI_MODEL_ALIASES: dict[tuple[str | None, str], str] = {
|
|
32
|
+
("minimax", "MiniMax Token 2.7"): "MiniMax-M2.7",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Devin2PiRunResult:
|
|
38
|
+
response_model: BaseModel
|
|
39
|
+
prompt_payload: dict[str, Any]
|
|
40
|
+
invocation: LlmInvocationResult
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Devin2PiRunnerError(RuntimeError):
|
|
44
|
+
"""Raised when the Devin 2.0 PI harness fails or returns invalid output."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _persist_context_artifact(*, repo_root: Path, stage_name: str, context_payload: dict[str, Any]) -> Path:
|
|
48
|
+
artifact_root = repo_root / ".devflow" / "agent_debug" / "devin2_context_artifacts"
|
|
49
|
+
artifact_root.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
safe_stage_name = re.sub(r"[^A-Za-z0-9_.-]+", "_", stage_name).strip("._") or "stage"
|
|
51
|
+
artifact_path = artifact_root / f"{safe_stage_name}.json"
|
|
52
|
+
artifact_path.write_text(json.dumps(context_payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
53
|
+
return artifact_path
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _extract_response_json(stdout: str) -> Any:
|
|
57
|
+
extracted = _extract_json(stdout)
|
|
58
|
+
if extracted is None:
|
|
59
|
+
raise Devin2PiRunnerError("Devin 2.0 PI harness returned no JSON payload.")
|
|
60
|
+
try:
|
|
61
|
+
return json.loads(extracted)
|
|
62
|
+
except json.JSONDecodeError as exc:
|
|
63
|
+
raise Devin2PiRunnerError(f"Devin 2.0 PI harness returned invalid JSON: {exc}") from exc
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _build_devin2_prompt_payload(
|
|
67
|
+
*,
|
|
68
|
+
repo_root: Path,
|
|
69
|
+
route_arm: Literal["ideation", "insight", "iterate"],
|
|
70
|
+
stage_name: str,
|
|
71
|
+
context_payload: dict[str, Any],
|
|
72
|
+
operational_guidance: list[str],
|
|
73
|
+
output_model: type[BaseModel],
|
|
74
|
+
) -> dict[str, Any]:
|
|
75
|
+
agent_prompt = build_devin2_agent_prompt_payload(repo_root=repo_root, route_arm=route_arm)
|
|
76
|
+
prompt_payload = build_response_prompt_payload(
|
|
77
|
+
stage_name=stage_name,
|
|
78
|
+
context_payload=context_payload,
|
|
79
|
+
operational_guidance=operational_guidance,
|
|
80
|
+
output_model=output_model,
|
|
81
|
+
agent_prompt=agent_prompt,
|
|
82
|
+
)
|
|
83
|
+
serialized = json.dumps(prompt_payload, indent=2, sort_keys=True)
|
|
84
|
+
if len(serialized) <= _MAX_PROMPT_CHARS:
|
|
85
|
+
return prompt_payload
|
|
86
|
+
|
|
87
|
+
artifact_path = _persist_context_artifact(
|
|
88
|
+
repo_root=repo_root,
|
|
89
|
+
stage_name=stage_name,
|
|
90
|
+
context_payload=context_payload,
|
|
91
|
+
)
|
|
92
|
+
return build_artifact_reference_response_prompt_payload(
|
|
93
|
+
stage_name=stage_name,
|
|
94
|
+
operational_guidance=operational_guidance,
|
|
95
|
+
output_model=output_model,
|
|
96
|
+
agent_prompt=agent_prompt,
|
|
97
|
+
artifact_path=artifact_path,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _normalize_pi_model_name(*, provider: str | None, model: str | None) -> str | None:
|
|
104
|
+
normalized = str(model or "").strip()
|
|
105
|
+
if not normalized:
|
|
106
|
+
return None
|
|
107
|
+
return _PI_MODEL_ALIASES.get((provider, normalized), normalized)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _resolve_pi_model_spec() -> str | None:
|
|
111
|
+
provider: str | None = None
|
|
112
|
+
model: str | None = None
|
|
113
|
+
try:
|
|
114
|
+
overrides = resolve_api_tier_request_overrides(tier="light")
|
|
115
|
+
provider = str(overrides.get("provider") or "").strip().lower() or None
|
|
116
|
+
model = str(overrides.get("model") or "").strip() or None
|
|
117
|
+
except Exception:
|
|
118
|
+
model = resolve_canonical_light_tier_model()
|
|
119
|
+
normalized_model = _normalize_pi_model_name(provider=provider, model=model)
|
|
120
|
+
if provider and normalized_model and "/" not in normalized_model:
|
|
121
|
+
return f"{provider}/{normalized_model}"
|
|
122
|
+
return normalized_model
|
|
123
|
+
|
|
124
|
+
def run_devin2_pi_agent(
|
|
125
|
+
*,
|
|
126
|
+
repo_root: Path,
|
|
127
|
+
stage_name: str,
|
|
128
|
+
route_arm: Literal["ideation", "insight", "iterate"],
|
|
129
|
+
context_payload: dict[str, Any],
|
|
130
|
+
operational_guidance: list[str],
|
|
131
|
+
output_model: type[BaseModel],
|
|
132
|
+
timeout_seconds: int | None = None,
|
|
133
|
+
) -> Devin2PiRunResult:
|
|
134
|
+
"""Run the Devin 2.0 prompt payload through the PI CLI harness.
|
|
135
|
+
|
|
136
|
+
This seam hard-pins the execution transport to the PI binary itself while
|
|
137
|
+
explicitly passing the canonical light-tier model from the user-managed
|
|
138
|
+
Supabase row ``devflow_settings.settings.tiers.light`` (falling back only
|
|
139
|
+
to the synced global DevFlow view when Supabase is unavailable). That keeps
|
|
140
|
+
Devin 2.0 PI runs off repo-local / legacy local config resolution.
|
|
141
|
+
"""
|
|
142
|
+
repo_root.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
prompt_payload = _build_devin2_prompt_payload(
|
|
144
|
+
repo_root=repo_root,
|
|
145
|
+
route_arm=route_arm,
|
|
146
|
+
stage_name=stage_name,
|
|
147
|
+
context_payload=context_payload,
|
|
148
|
+
operational_guidance=operational_guidance,
|
|
149
|
+
output_model=output_model,
|
|
150
|
+
)
|
|
151
|
+
prompt_text = json.dumps(prompt_payload, indent=2, sort_keys=True)
|
|
152
|
+
canonical_light_model = _resolve_pi_model_spec()
|
|
153
|
+
# Load the Devin-specific extension, which now applies its own explicit
|
|
154
|
+
# allowlist via PI's setActiveTools(...) seam.
|
|
155
|
+
pi_base_cmd = _PI_BASE_CMD_WITH_DEVFLOW_TOOLS
|
|
156
|
+
|
|
157
|
+
_MAX_PI_ATTEMPTS = 3
|
|
158
|
+
last_error: Exception | None = None
|
|
159
|
+
invocation = None
|
|
160
|
+
for _attempt in range(_MAX_PI_ATTEMPTS):
|
|
161
|
+
invocation = invoke_llm(
|
|
162
|
+
LlmInvocationRequest(
|
|
163
|
+
purpose=stage_name,
|
|
164
|
+
strength="light",
|
|
165
|
+
repo_root=repo_root,
|
|
166
|
+
prompt=prompt_text,
|
|
167
|
+
prompt_payload=prompt_payload,
|
|
168
|
+
delivery_model="final_only",
|
|
169
|
+
interaction_model="agentic",
|
|
170
|
+
response_contract="json_only",
|
|
171
|
+
timeout_seconds=timeout_seconds,
|
|
172
|
+
base_cmd=pi_base_cmd,
|
|
173
|
+
delivery=_PI_DELIVERY,
|
|
174
|
+
provider=_PI_PROVIDER,
|
|
175
|
+
model=canonical_light_model,
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
if not invocation.ok:
|
|
179
|
+
last_error = Devin2PiRunnerError(invocation.stderr or invocation.stdout or "Devin 2.0 PI harness failed.")
|
|
180
|
+
continue
|
|
181
|
+
# Check for parseable JSON in the response
|
|
182
|
+
parsed_json = invocation.parsed_json
|
|
183
|
+
if parsed_json is None:
|
|
184
|
+
try:
|
|
185
|
+
parsed_json = _extract_response_json(invocation.stdout)
|
|
186
|
+
except Devin2PiRunnerError as exc:
|
|
187
|
+
last_error = exc
|
|
188
|
+
continue
|
|
189
|
+
break
|
|
190
|
+
else:
|
|
191
|
+
if last_error is not None:
|
|
192
|
+
raise last_error
|
|
193
|
+
raise Devin2PiRunnerError("Devin 2.0 PI harness failed after all retry attempts.")
|
|
194
|
+
assert invocation is not None
|
|
195
|
+
try:
|
|
196
|
+
response_model = output_model.model_validate(parsed_json)
|
|
197
|
+
except ValidationError as exc:
|
|
198
|
+
raise Devin2PiRunnerError(f"Devin 2.0 PI harness returned schema-invalid JSON: {exc}") from exc
|
|
199
|
+
|
|
200
|
+
return Devin2PiRunResult(
|
|
201
|
+
response_model=response_model,
|
|
202
|
+
prompt_payload=prompt_payload,
|
|
203
|
+
invocation=invocation,
|
|
204
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from devin.dag_two_arm import run_devin_two_arm_dag as run_devin_chat_dag
|
|
7
|
+
from .stores.execution_store import ExecutionStore
|
|
8
|
+
|
|
9
|
+
DEVIN_QUEUE_NAME = "assistant.devin"
|
|
10
|
+
DEVIN_CHAT_TASK_KIND = "devin.chat"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def enqueue_devin_chat_task(
|
|
14
|
+
*,
|
|
15
|
+
store: ExecutionStore,
|
|
16
|
+
project_id: str | None,
|
|
17
|
+
idea_id: str,
|
|
18
|
+
repo_root: Path,
|
|
19
|
+
text: str | None,
|
|
20
|
+
source_path: Path | None,
|
|
21
|
+
max_stories: int,
|
|
22
|
+
planes: list[str],
|
|
23
|
+
source_run_id: str | None = None,
|
|
24
|
+
response_mode_label: str | None = None,
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
idempotency_key = f"{idea_id}:{','.join(sorted(planes))}:{max_stories}:{text or ''}:{str(source_path or '')}:{response_mode_label or ''}"
|
|
27
|
+
return store.enqueue_task(
|
|
28
|
+
project_id=project_id,
|
|
29
|
+
queue_name=DEVIN_QUEUE_NAME,
|
|
30
|
+
task_kind=DEVIN_CHAT_TASK_KIND,
|
|
31
|
+
idempotency_key=idempotency_key,
|
|
32
|
+
source_run_id=source_run_id,
|
|
33
|
+
input_payload={
|
|
34
|
+
"idea_id": idea_id,
|
|
35
|
+
"text": text,
|
|
36
|
+
"source_path": str(source_path) if source_path else None,
|
|
37
|
+
"max_stories": max_stories,
|
|
38
|
+
"planes": planes,
|
|
39
|
+
"response_mode_label": response_mode_label,
|
|
40
|
+
},
|
|
41
|
+
context={"client": "devin", "workflow": "devin_chat"},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def handle_devin_chat_task(store: ExecutionStore, repo_root: Path, task: dict[str, Any]) -> dict[str, Any]:
|
|
46
|
+
payload = dict(task.get("input") or {})
|
|
47
|
+
task_id = str(task["task_id"])
|
|
48
|
+
store.record_task_step(task_id=task_id, step_name="devin_chat.run", status="started", payload=payload)
|
|
49
|
+
result = run_devin_chat_dag(
|
|
50
|
+
repo_root=repo_root,
|
|
51
|
+
store=store,
|
|
52
|
+
idea_id=str(payload.get("idea_id") or ""),
|
|
53
|
+
text=payload.get("text"),
|
|
54
|
+
source_path=Path(str(payload["source_path"])) if payload.get("source_path") else None,
|
|
55
|
+
max_stories=int(payload.get("max_stories") or 3),
|
|
56
|
+
planes=[str(item) for item in (payload.get("planes") or ["api"])],
|
|
57
|
+
response_mode_label=(None if payload.get("response_mode_label") is None else str(payload.get("response_mode_label"))),
|
|
58
|
+
)
|
|
59
|
+
response = {
|
|
60
|
+
"idea_id": str(payload.get("idea_id") or ""),
|
|
61
|
+
"run_id": result.run_id,
|
|
62
|
+
"exit_code": result.exit_code,
|
|
63
|
+
"pipeline_dir": str(result.pipeline_dir),
|
|
64
|
+
"message": result.message,
|
|
65
|
+
"outcome": result.outcome,
|
|
66
|
+
}
|
|
67
|
+
store.emit_task_message(task_id=task_id, message_kind="result", stream="assistant", payload=response)
|
|
68
|
+
store.record_task_step(task_id=task_id, step_name="devin_chat.run", status="finished", payload=response)
|
|
69
|
+
return response
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Prompt anti-patterns
|
|
2
|
+
|
|
3
|
+
This is the canonical home for prompt anti-pattern notes in `devflow_engine`.
|
|
4
|
+
|
|
5
|
+
Scope split:
|
|
6
|
+
- prompt design guidance and anti-patterns live under `docs/prompts/`
|
|
7
|
+
- runtime skill artifacts live under `src/devflow_engine/skills/...` as described in `docs/devflow-skill-registry-spec.md`
|
|
8
|
+
|
|
9
|
+
Keep this list short and concrete. Add an entry when it changes prompt quality or agent correctness in a recurring way.
|
|
10
|
+
|
|
11
|
+
## Context smuggling
|
|
12
|
+
|
|
13
|
+
**Definition:** leaking author-side orchestration, internal jargon, or non-local workflow context into a node/agent prompt that does not actually have that local context.
|
|
14
|
+
|
|
15
|
+
Common examples:
|
|
16
|
+
- telling an agent to "use the output from Node 1" or "continue what Node 2 already established" when that material is not present in the prompt payload
|
|
17
|
+
- referencing GenAI DAG internals, route names, or pipeline stage assumptions that are meaningful to the prompt author but not to the receiving agent
|
|
18
|
+
- writing prompts that depend on hidden repo/process knowledge instead of the explicitly provided task inputs
|
|
19
|
+
|
|
20
|
+
Why it is harmful:
|
|
21
|
+
- creates fake coherence: the prompt sounds specific, but the agent cannot actually ground the references
|
|
22
|
+
- encourages hallucinated continuity and invented dependencies
|
|
23
|
+
- makes prompts brittle when nodes are reused, reordered, or evaluated in isolation
|
|
24
|
+
- hides the real contract the node needs in order to work correctly
|
|
25
|
+
|
|
26
|
+
Preferred correction:
|
|
27
|
+
- restate the local task in terms of the inputs the node actually receives
|
|
28
|
+
- pass required upstream artifacts explicitly instead of naming invisible pipeline steps
|
|
29
|
+
- replace internal orchestration references with bounded, self-contained instructions
|
|
30
|
+
- if an agent truly needs workflow metadata, include that metadata as explicit input, not implied context
|
|
31
|
+
|
|
32
|
+
## Hidden approval boundary
|
|
33
|
+
|
|
34
|
+
Stub: prompts that imply permission to proceed across a human approval gate without explicitly stating whether approval has been given.
|
|
35
|
+
|
|
36
|
+
## State inflation
|
|
37
|
+
|
|
38
|
+
Stub: prompts that collapse distinct states such as drafted, persisted, queued, started, and completed into one vague notion of "in progress".
|
|
39
|
+
|
|
40
|
+
## Tool-shaped prompt leakage
|
|
41
|
+
|
|
42
|
+
Stub: prompts that bake in harness/tool mechanics too early, instead of expressing the task and only layering tool-specific constraints where they are actually required.
|