tapps-agents 3.5.40__py3-none-any.whl → 3.6.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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -219
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/config.py +33 -0
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_executor.py +2337 -2196
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/message_formatter.py +188 -188
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -111
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,449 +1,449 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Real-Time Status Monitoring for Workflow Execution
|
|
3
|
-
|
|
4
|
-
Monitors workflow status files and triggers progress updates when changes occur.
|
|
5
|
-
Epic 8 / Story 8.2: Real-Time Status Monitoring
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import asyncio
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
from collections.abc import Callable
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from datetime import datetime
|
|
14
|
-
from enum import Enum
|
|
15
|
-
from pathlib import Path
|
|
16
|
-
from typing import Any
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class StatusChangeEvent(Enum):
|
|
22
|
-
"""Status change event types."""
|
|
23
|
-
|
|
24
|
-
STEP_STARTED = "step_started"
|
|
25
|
-
STEP_PROGRESS = "step_progress"
|
|
26
|
-
STEP_COMPLETED = "step_completed"
|
|
27
|
-
STEP_FAILED = "step_failed"
|
|
28
|
-
WORKFLOW_COMPLETED = "workflow_completed"
|
|
29
|
-
WORKFLOW_FAILED = "workflow_failed"
|
|
30
|
-
WORKFLOW_STARTED = "workflow_started"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class StatusChange:
|
|
35
|
-
"""Represents a status change event."""
|
|
36
|
-
|
|
37
|
-
event_type: StatusChangeEvent
|
|
38
|
-
workflow_id: str
|
|
39
|
-
timestamp: datetime
|
|
40
|
-
step_id: str | None = None
|
|
41
|
-
agent: str | None = None
|
|
42
|
-
action: str | None = None
|
|
43
|
-
error: str | None = None
|
|
44
|
-
metadata: dict[str, Any] | None = None
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class StatusFileMonitor:
|
|
48
|
-
"""Monitors workflow status files for changes."""
|
|
49
|
-
|
|
50
|
-
def __init__(
|
|
51
|
-
self,
|
|
52
|
-
state_dir: Path,
|
|
53
|
-
poll_interval_seconds: float = 2.0,
|
|
54
|
-
on_status_change: Callable[[StatusChange], None] | None = None,
|
|
55
|
-
event_bus: Any | None = None,
|
|
56
|
-
):
|
|
57
|
-
"""
|
|
58
|
-
Initialize status file monitor.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
state_dir: Directory containing workflow state files
|
|
62
|
-
poll_interval_seconds: How often to check for changes
|
|
63
|
-
on_status_change: Callback when status changes detected
|
|
64
|
-
event_bus: Optional event bus for publishing events (Phase 2)
|
|
65
|
-
"""
|
|
66
|
-
self.state_dir = Path(state_dir)
|
|
67
|
-
self.poll_interval = poll_interval_seconds
|
|
68
|
-
self.on_status_change = on_status_change
|
|
69
|
-
self.event_bus = event_bus
|
|
70
|
-
self.monitored_workflows: dict[str, dict[str, Any]] = {}
|
|
71
|
-
self.running = False
|
|
72
|
-
self._monitor_task: asyncio.Task | None = None
|
|
73
|
-
|
|
74
|
-
async def start(self) -> None:
|
|
75
|
-
"""Start monitoring status files."""
|
|
76
|
-
if self.running:
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
self.running = True
|
|
80
|
-
self._monitor_task = asyncio.create_task(self._monitor_loop())
|
|
81
|
-
logger.info("Status file monitor started")
|
|
82
|
-
|
|
83
|
-
async def stop(self) -> None:
|
|
84
|
-
"""Stop monitoring status files."""
|
|
85
|
-
self.running = False
|
|
86
|
-
if self._monitor_task:
|
|
87
|
-
self._monitor_task.cancel()
|
|
88
|
-
try:
|
|
89
|
-
await self._monitor_task
|
|
90
|
-
except asyncio.CancelledError:
|
|
91
|
-
pass
|
|
92
|
-
logger.info("Status file monitor stopped")
|
|
93
|
-
|
|
94
|
-
async def monitor_workflow(self, workflow_id: str) -> None:
|
|
95
|
-
"""
|
|
96
|
-
Start monitoring a specific workflow.
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
workflow_id: Workflow ID to monitor
|
|
100
|
-
"""
|
|
101
|
-
state_file = self.state_dir / f"{workflow_id}.json"
|
|
102
|
-
if state_file.exists():
|
|
103
|
-
# Load initial state
|
|
104
|
-
try:
|
|
105
|
-
state = self._load_state(state_file)
|
|
106
|
-
if state:
|
|
107
|
-
self.monitored_workflows[workflow_id] = {
|
|
108
|
-
"state": state,
|
|
109
|
-
"last_modified": state_file.stat().st_mtime,
|
|
110
|
-
}
|
|
111
|
-
except Exception as e:
|
|
112
|
-
logger.error(f"Failed to load initial state for {workflow_id}: {e}")
|
|
113
|
-
|
|
114
|
-
async def _monitor_loop(self) -> None:
|
|
115
|
-
"""Main monitoring loop."""
|
|
116
|
-
while self.running:
|
|
117
|
-
try:
|
|
118
|
-
await self._check_status_files()
|
|
119
|
-
await asyncio.sleep(self.poll_interval)
|
|
120
|
-
except asyncio.CancelledError:
|
|
121
|
-
break
|
|
122
|
-
except Exception as e:
|
|
123
|
-
logger.error(f"Error in monitor loop: {e}")
|
|
124
|
-
await asyncio.sleep(self.poll_interval)
|
|
125
|
-
|
|
126
|
-
async def _check_status_files(self) -> None:
|
|
127
|
-
"""Check all monitored workflow status files for changes."""
|
|
128
|
-
from .file_utils import is_file_stable
|
|
129
|
-
|
|
130
|
-
# Find all state files
|
|
131
|
-
if not self.state_dir.exists():
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
state_files = list(self.state_dir.glob("*.json"))
|
|
135
|
-
# Exclude history directory, metadata files, and last.json
|
|
136
|
-
state_files = [
|
|
137
|
-
f for f in state_files
|
|
138
|
-
if "history" not in str(f)
|
|
139
|
-
and not f.name.endswith(".meta.json")
|
|
140
|
-
and f.name != "last.json"
|
|
141
|
-
]
|
|
142
|
-
|
|
143
|
-
for state_file in state_files:
|
|
144
|
-
workflow_id = state_file.stem
|
|
145
|
-
try:
|
|
146
|
-
# Skip files that are too new or too small (likely incomplete)
|
|
147
|
-
if not is_file_stable(state_file, min_age_seconds=2.0):
|
|
148
|
-
continue
|
|
149
|
-
|
|
150
|
-
# Check if file was modified
|
|
151
|
-
current_mtime = state_file.stat().st_mtime
|
|
152
|
-
|
|
153
|
-
if workflow_id in self.monitored_workflows:
|
|
154
|
-
last_mtime = self.monitored_workflows[workflow_id].get(
|
|
155
|
-
"last_modified", 0
|
|
156
|
-
)
|
|
157
|
-
if current_mtime > last_mtime:
|
|
158
|
-
# File changed, detect what changed
|
|
159
|
-
await self._detect_changes(workflow_id, state_file)
|
|
160
|
-
else:
|
|
161
|
-
# New workflow, start monitoring
|
|
162
|
-
await self.monitor_workflow(workflow_id)
|
|
163
|
-
|
|
164
|
-
except Exception as e:
|
|
165
|
-
# Log as debug to avoid spam - some files may be in transition
|
|
166
|
-
logger.debug(f"Error checking status file {state_file}: {e}")
|
|
167
|
-
|
|
168
|
-
async def _detect_changes(
|
|
169
|
-
self, workflow_id: str, state_file: Path
|
|
170
|
-
) -> None:
|
|
171
|
-
"""Detect what changed in workflow state."""
|
|
172
|
-
try:
|
|
173
|
-
new_state = self._load_state(state_file)
|
|
174
|
-
if not new_state:
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
old_state_data = self.monitored_workflows.get(workflow_id, {}).get(
|
|
178
|
-
"state"
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
# Update stored state
|
|
182
|
-
self.monitored_workflows[workflow_id] = {
|
|
183
|
-
"state": new_state,
|
|
184
|
-
"last_modified": state_file.stat().st_mtime,
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if not old_state_data:
|
|
188
|
-
# New workflow
|
|
189
|
-
if self.on_status_change:
|
|
190
|
-
self.on_status_change(
|
|
191
|
-
StatusChange(
|
|
192
|
-
event_type=StatusChangeEvent.WORKFLOW_STARTED,
|
|
193
|
-
workflow_id=workflow_id,
|
|
194
|
-
timestamp=datetime.now(),
|
|
195
|
-
)
|
|
196
|
-
)
|
|
197
|
-
# Publish event to event bus (Phase 2)
|
|
198
|
-
if self.event_bus:
|
|
199
|
-
from .events import EventType, WorkflowEvent
|
|
200
|
-
await self.event_bus.publish(
|
|
201
|
-
WorkflowEvent(
|
|
202
|
-
event_type=EventType.WORKFLOW_STARTED,
|
|
203
|
-
workflow_id=workflow_id,
|
|
204
|
-
step_id=None,
|
|
205
|
-
data={},
|
|
206
|
-
timestamp=datetime.now(),
|
|
207
|
-
)
|
|
208
|
-
)
|
|
209
|
-
return
|
|
210
|
-
|
|
211
|
-
# Compare states to detect changes
|
|
212
|
-
old_state = old_state_data
|
|
213
|
-
|
|
214
|
-
# Check workflow status changes
|
|
215
|
-
if new_state.get("status") != old_state.get("status"):
|
|
216
|
-
if new_state.get("status") == "completed":
|
|
217
|
-
if self.on_status_change:
|
|
218
|
-
self.on_status_change(
|
|
219
|
-
StatusChange(
|
|
220
|
-
event_type=StatusChangeEvent.WORKFLOW_COMPLETED,
|
|
221
|
-
workflow_id=workflow_id,
|
|
222
|
-
timestamp=datetime.now(),
|
|
223
|
-
)
|
|
224
|
-
)
|
|
225
|
-
# Publish event to event bus (Phase 2)
|
|
226
|
-
if self.event_bus:
|
|
227
|
-
from .events import EventType, WorkflowEvent
|
|
228
|
-
await self.event_bus.publish(
|
|
229
|
-
WorkflowEvent(
|
|
230
|
-
event_type=EventType.WORKFLOW_COMPLETED,
|
|
231
|
-
workflow_id=workflow_id,
|
|
232
|
-
step_id=None,
|
|
233
|
-
data={},
|
|
234
|
-
timestamp=datetime.now(),
|
|
235
|
-
)
|
|
236
|
-
)
|
|
237
|
-
elif new_state.get("status") == "failed":
|
|
238
|
-
if self.on_status_change:
|
|
239
|
-
self.on_status_change(
|
|
240
|
-
StatusChange(
|
|
241
|
-
event_type=StatusChangeEvent.WORKFLOW_FAILED,
|
|
242
|
-
workflow_id=workflow_id,
|
|
243
|
-
timestamp=datetime.now(),
|
|
244
|
-
error=new_state.get("error"),
|
|
245
|
-
)
|
|
246
|
-
)
|
|
247
|
-
# Publish event to event bus (Phase 2)
|
|
248
|
-
if self.event_bus:
|
|
249
|
-
from .events import EventType, WorkflowEvent
|
|
250
|
-
await self.event_bus.publish(
|
|
251
|
-
WorkflowEvent(
|
|
252
|
-
event_type=EventType.WORKFLOW_FAILED,
|
|
253
|
-
workflow_id=workflow_id,
|
|
254
|
-
step_id=None,
|
|
255
|
-
data={"error": new_state.get("error")},
|
|
256
|
-
timestamp=datetime.now(),
|
|
257
|
-
)
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
# Check step execution changes
|
|
261
|
-
old_step_executions = {
|
|
262
|
-
se["step_id"]: se
|
|
263
|
-
for se in old_state.get("step_executions", [])
|
|
264
|
-
}
|
|
265
|
-
new_step_executions = {
|
|
266
|
-
se["step_id"]: se
|
|
267
|
-
for se in new_state.get("step_executions", [])
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
# Detect new step executions
|
|
271
|
-
for step_id, step_exec in new_step_executions.items():
|
|
272
|
-
old_step_exec = old_step_executions.get(step_id)
|
|
273
|
-
if not old_step_exec:
|
|
274
|
-
# New step started
|
|
275
|
-
if self.on_status_change:
|
|
276
|
-
self.on_status_change(
|
|
277
|
-
StatusChange(
|
|
278
|
-
event_type=StatusChangeEvent.STEP_STARTED,
|
|
279
|
-
workflow_id=workflow_id,
|
|
280
|
-
timestamp=datetime.now(),
|
|
281
|
-
step_id=step_id,
|
|
282
|
-
agent=step_exec.get("agent"),
|
|
283
|
-
action=step_exec.get("action"),
|
|
284
|
-
)
|
|
285
|
-
)
|
|
286
|
-
# Publish event to event bus (Phase 2)
|
|
287
|
-
if self.event_bus:
|
|
288
|
-
from .events import EventType, WorkflowEvent
|
|
289
|
-
await self.event_bus.publish(
|
|
290
|
-
WorkflowEvent(
|
|
291
|
-
event_type=EventType.STEP_STARTED,
|
|
292
|
-
workflow_id=workflow_id,
|
|
293
|
-
step_id=step_id,
|
|
294
|
-
data={
|
|
295
|
-
"agent": step_exec.get("agent"),
|
|
296
|
-
"action": step_exec.get("action"),
|
|
297
|
-
},
|
|
298
|
-
timestamp=datetime.now(),
|
|
299
|
-
)
|
|
300
|
-
)
|
|
301
|
-
elif step_exec.get("status") != old_step_exec.get("status"):
|
|
302
|
-
# Step status changed
|
|
303
|
-
if step_exec.get("status") == "completed":
|
|
304
|
-
if self.on_status_change:
|
|
305
|
-
self.on_status_change(
|
|
306
|
-
StatusChange(
|
|
307
|
-
event_type=StatusChangeEvent.STEP_COMPLETED,
|
|
308
|
-
workflow_id=workflow_id,
|
|
309
|
-
timestamp=datetime.now(),
|
|
310
|
-
step_id=step_id,
|
|
311
|
-
agent=step_exec.get("agent"),
|
|
312
|
-
action=step_exec.get("action"),
|
|
313
|
-
)
|
|
314
|
-
)
|
|
315
|
-
# Publish event to event bus (Phase 2)
|
|
316
|
-
if self.event_bus:
|
|
317
|
-
from .events import EventType, WorkflowEvent
|
|
318
|
-
await self.event_bus.publish(
|
|
319
|
-
WorkflowEvent(
|
|
320
|
-
event_type=EventType.STEP_COMPLETED,
|
|
321
|
-
workflow_id=workflow_id,
|
|
322
|
-
step_id=step_id,
|
|
323
|
-
data={
|
|
324
|
-
"agent": step_exec.get("agent"),
|
|
325
|
-
"action": step_exec.get("action"),
|
|
326
|
-
},
|
|
327
|
-
timestamp=datetime.now(),
|
|
328
|
-
)
|
|
329
|
-
)
|
|
330
|
-
elif step_exec.get("status") == "failed":
|
|
331
|
-
if self.on_status_change:
|
|
332
|
-
self.on_status_change(
|
|
333
|
-
StatusChange(
|
|
334
|
-
event_type=StatusChangeEvent.STEP_FAILED,
|
|
335
|
-
workflow_id=workflow_id,
|
|
336
|
-
timestamp=datetime.now(),
|
|
337
|
-
step_id=step_id,
|
|
338
|
-
agent=step_exec.get("agent"),
|
|
339
|
-
action=step_exec.get("action"),
|
|
340
|
-
error=step_exec.get("error"),
|
|
341
|
-
)
|
|
342
|
-
)
|
|
343
|
-
# Publish event to event bus (Phase 2)
|
|
344
|
-
if self.event_bus:
|
|
345
|
-
from .events import EventType, WorkflowEvent
|
|
346
|
-
await self.event_bus.publish(
|
|
347
|
-
WorkflowEvent(
|
|
348
|
-
event_type=EventType.STEP_FAILED,
|
|
349
|
-
workflow_id=workflow_id,
|
|
350
|
-
step_id=step_id,
|
|
351
|
-
data={
|
|
352
|
-
"agent": step_exec.get("agent"),
|
|
353
|
-
"action": step_exec.get("action"),
|
|
354
|
-
"error": step_exec.get("error"),
|
|
355
|
-
},
|
|
356
|
-
timestamp=datetime.now(),
|
|
357
|
-
)
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
except Exception as e:
|
|
361
|
-
logger.error(f"Error detecting changes for {workflow_id}: {e}")
|
|
362
|
-
|
|
363
|
-
def _load_state(self, state_file: Path) -> dict[str, Any] | None:
|
|
364
|
-
"""
|
|
365
|
-
Load workflow state from file with safe loading and validation.
|
|
366
|
-
|
|
367
|
-
Args:
|
|
368
|
-
state_file: Path to state file
|
|
369
|
-
|
|
370
|
-
Returns:
|
|
371
|
-
State dictionary or None if failed
|
|
372
|
-
"""
|
|
373
|
-
from .file_utils import safe_load_json, is_file_stable
|
|
374
|
-
|
|
375
|
-
# Skip files that are too new (likely still being written)
|
|
376
|
-
if not is_file_stable(state_file, min_age_seconds=2.0):
|
|
377
|
-
return None
|
|
378
|
-
|
|
379
|
-
# Use safe loading with retry logic
|
|
380
|
-
state = safe_load_json(
|
|
381
|
-
state_file,
|
|
382
|
-
retries=2, # Fewer retries for monitoring (we'll check again next poll)
|
|
383
|
-
backoff=0.5,
|
|
384
|
-
min_age_seconds=2.0,
|
|
385
|
-
min_size=100, # Minimum reasonable state file size
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
if state is None:
|
|
389
|
-
# Only log as debug to avoid spam - incomplete files are expected during writes
|
|
390
|
-
logger.debug(f"Could not load state file {state_file} (may be incomplete)")
|
|
391
|
-
|
|
392
|
-
return state
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
class BackgroundAgentMonitor:
|
|
396
|
-
"""Monitors Background Agent execution status."""
|
|
397
|
-
|
|
398
|
-
def __init__(
|
|
399
|
-
self,
|
|
400
|
-
worktree_base: Path,
|
|
401
|
-
on_status_change: Callable[[StatusChange], None] | None = None,
|
|
402
|
-
):
|
|
403
|
-
"""
|
|
404
|
-
Initialize Background Agent monitor.
|
|
405
|
-
|
|
406
|
-
Args:
|
|
407
|
-
worktree_base: Base directory for worktrees
|
|
408
|
-
on_status_change: Callback when status changes detected
|
|
409
|
-
"""
|
|
410
|
-
self.worktree_base = Path(worktree_base)
|
|
411
|
-
self.on_status_change = on_status_change
|
|
412
|
-
|
|
413
|
-
async def monitor_worktree(
|
|
414
|
-
self, worktree_path: Path, workflow_id: str, step_id: str
|
|
415
|
-
) -> None:
|
|
416
|
-
"""
|
|
417
|
-
Monitor a specific worktree for Background Agent completion.
|
|
418
|
-
|
|
419
|
-
Args:
|
|
420
|
-
worktree_path: Path to worktree
|
|
421
|
-
workflow_id: Workflow ID
|
|
422
|
-
step_id: Step ID
|
|
423
|
-
"""
|
|
424
|
-
# Check for progress files or completion markers
|
|
425
|
-
progress_file = worktree_path / ".tapps-agents" / "progress.json"
|
|
426
|
-
completion_file = worktree_path / ".tapps-agents" / "completed.txt"
|
|
427
|
-
|
|
428
|
-
if completion_file.exists():
|
|
429
|
-
if self.on_status_change:
|
|
430
|
-
self.on_status_change(
|
|
431
|
-
StatusChange(
|
|
432
|
-
event_type=StatusChangeEvent.STEP_COMPLETED,
|
|
433
|
-
workflow_id=workflow_id,
|
|
434
|
-
timestamp=datetime.now(),
|
|
435
|
-
step_id=step_id,
|
|
436
|
-
)
|
|
437
|
-
)
|
|
438
|
-
elif progress_file.exists():
|
|
439
|
-
# Background Agent is running, could emit progress events
|
|
440
|
-
if self.on_status_change:
|
|
441
|
-
self.on_status_change(
|
|
442
|
-
StatusChange(
|
|
443
|
-
event_type=StatusChangeEvent.STEP_PROGRESS,
|
|
444
|
-
workflow_id=workflow_id,
|
|
445
|
-
timestamp=datetime.now(),
|
|
446
|
-
step_id=step_id,
|
|
447
|
-
)
|
|
448
|
-
)
|
|
449
|
-
|
|
1
|
+
"""
|
|
2
|
+
Real-Time Status Monitoring for Workflow Execution
|
|
3
|
+
|
|
4
|
+
Monitors workflow status files and triggers progress updates when changes occur.
|
|
5
|
+
Epic 8 / Story 8.2: Real-Time Status Monitoring
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StatusChangeEvent(Enum):
|
|
22
|
+
"""Status change event types."""
|
|
23
|
+
|
|
24
|
+
STEP_STARTED = "step_started"
|
|
25
|
+
STEP_PROGRESS = "step_progress"
|
|
26
|
+
STEP_COMPLETED = "step_completed"
|
|
27
|
+
STEP_FAILED = "step_failed"
|
|
28
|
+
WORKFLOW_COMPLETED = "workflow_completed"
|
|
29
|
+
WORKFLOW_FAILED = "workflow_failed"
|
|
30
|
+
WORKFLOW_STARTED = "workflow_started"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class StatusChange:
|
|
35
|
+
"""Represents a status change event."""
|
|
36
|
+
|
|
37
|
+
event_type: StatusChangeEvent
|
|
38
|
+
workflow_id: str
|
|
39
|
+
timestamp: datetime
|
|
40
|
+
step_id: str | None = None
|
|
41
|
+
agent: str | None = None
|
|
42
|
+
action: str | None = None
|
|
43
|
+
error: str | None = None
|
|
44
|
+
metadata: dict[str, Any] | None = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class StatusFileMonitor:
|
|
48
|
+
"""Monitors workflow status files for changes."""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
state_dir: Path,
|
|
53
|
+
poll_interval_seconds: float = 2.0,
|
|
54
|
+
on_status_change: Callable[[StatusChange], None] | None = None,
|
|
55
|
+
event_bus: Any | None = None,
|
|
56
|
+
):
|
|
57
|
+
"""
|
|
58
|
+
Initialize status file monitor.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
state_dir: Directory containing workflow state files
|
|
62
|
+
poll_interval_seconds: How often to check for changes
|
|
63
|
+
on_status_change: Callback when status changes detected
|
|
64
|
+
event_bus: Optional event bus for publishing events (Phase 2)
|
|
65
|
+
"""
|
|
66
|
+
self.state_dir = Path(state_dir)
|
|
67
|
+
self.poll_interval = poll_interval_seconds
|
|
68
|
+
self.on_status_change = on_status_change
|
|
69
|
+
self.event_bus = event_bus
|
|
70
|
+
self.monitored_workflows: dict[str, dict[str, Any]] = {}
|
|
71
|
+
self.running = False
|
|
72
|
+
self._monitor_task: asyncio.Task | None = None
|
|
73
|
+
|
|
74
|
+
async def start(self) -> None:
|
|
75
|
+
"""Start monitoring status files."""
|
|
76
|
+
if self.running:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
self.running = True
|
|
80
|
+
self._monitor_task = asyncio.create_task(self._monitor_loop())
|
|
81
|
+
logger.info("Status file monitor started")
|
|
82
|
+
|
|
83
|
+
async def stop(self) -> None:
|
|
84
|
+
"""Stop monitoring status files."""
|
|
85
|
+
self.running = False
|
|
86
|
+
if self._monitor_task:
|
|
87
|
+
self._monitor_task.cancel()
|
|
88
|
+
try:
|
|
89
|
+
await self._monitor_task
|
|
90
|
+
except asyncio.CancelledError:
|
|
91
|
+
pass
|
|
92
|
+
logger.info("Status file monitor stopped")
|
|
93
|
+
|
|
94
|
+
async def monitor_workflow(self, workflow_id: str) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Start monitoring a specific workflow.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
workflow_id: Workflow ID to monitor
|
|
100
|
+
"""
|
|
101
|
+
state_file = self.state_dir / f"{workflow_id}.json"
|
|
102
|
+
if state_file.exists():
|
|
103
|
+
# Load initial state
|
|
104
|
+
try:
|
|
105
|
+
state = self._load_state(state_file)
|
|
106
|
+
if state:
|
|
107
|
+
self.monitored_workflows[workflow_id] = {
|
|
108
|
+
"state": state,
|
|
109
|
+
"last_modified": state_file.stat().st_mtime,
|
|
110
|
+
}
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Failed to load initial state for {workflow_id}: {e}")
|
|
113
|
+
|
|
114
|
+
async def _monitor_loop(self) -> None:
|
|
115
|
+
"""Main monitoring loop."""
|
|
116
|
+
while self.running:
|
|
117
|
+
try:
|
|
118
|
+
await self._check_status_files()
|
|
119
|
+
await asyncio.sleep(self.poll_interval)
|
|
120
|
+
except asyncio.CancelledError:
|
|
121
|
+
break
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Error in monitor loop: {e}")
|
|
124
|
+
await asyncio.sleep(self.poll_interval)
|
|
125
|
+
|
|
126
|
+
async def _check_status_files(self) -> None:
|
|
127
|
+
"""Check all monitored workflow status files for changes."""
|
|
128
|
+
from .file_utils import is_file_stable
|
|
129
|
+
|
|
130
|
+
# Find all state files
|
|
131
|
+
if not self.state_dir.exists():
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
state_files = list(self.state_dir.glob("*.json"))
|
|
135
|
+
# Exclude history directory, metadata files, and last.json
|
|
136
|
+
state_files = [
|
|
137
|
+
f for f in state_files
|
|
138
|
+
if "history" not in str(f)
|
|
139
|
+
and not f.name.endswith(".meta.json")
|
|
140
|
+
and f.name != "last.json"
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
for state_file in state_files:
|
|
144
|
+
workflow_id = state_file.stem
|
|
145
|
+
try:
|
|
146
|
+
# Skip files that are too new or too small (likely incomplete)
|
|
147
|
+
if not is_file_stable(state_file, min_age_seconds=2.0):
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
# Check if file was modified
|
|
151
|
+
current_mtime = state_file.stat().st_mtime
|
|
152
|
+
|
|
153
|
+
if workflow_id in self.monitored_workflows:
|
|
154
|
+
last_mtime = self.monitored_workflows[workflow_id].get(
|
|
155
|
+
"last_modified", 0
|
|
156
|
+
)
|
|
157
|
+
if current_mtime > last_mtime:
|
|
158
|
+
# File changed, detect what changed
|
|
159
|
+
await self._detect_changes(workflow_id, state_file)
|
|
160
|
+
else:
|
|
161
|
+
# New workflow, start monitoring
|
|
162
|
+
await self.monitor_workflow(workflow_id)
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
# Log as debug to avoid spam - some files may be in transition
|
|
166
|
+
logger.debug(f"Error checking status file {state_file}: {e}")
|
|
167
|
+
|
|
168
|
+
async def _detect_changes(
|
|
169
|
+
self, workflow_id: str, state_file: Path
|
|
170
|
+
) -> None:
|
|
171
|
+
"""Detect what changed in workflow state."""
|
|
172
|
+
try:
|
|
173
|
+
new_state = self._load_state(state_file)
|
|
174
|
+
if not new_state:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
old_state_data = self.monitored_workflows.get(workflow_id, {}).get(
|
|
178
|
+
"state"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Update stored state
|
|
182
|
+
self.monitored_workflows[workflow_id] = {
|
|
183
|
+
"state": new_state,
|
|
184
|
+
"last_modified": state_file.stat().st_mtime,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if not old_state_data:
|
|
188
|
+
# New workflow
|
|
189
|
+
if self.on_status_change:
|
|
190
|
+
self.on_status_change(
|
|
191
|
+
StatusChange(
|
|
192
|
+
event_type=StatusChangeEvent.WORKFLOW_STARTED,
|
|
193
|
+
workflow_id=workflow_id,
|
|
194
|
+
timestamp=datetime.now(),
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
# Publish event to event bus (Phase 2)
|
|
198
|
+
if self.event_bus:
|
|
199
|
+
from .events import EventType, WorkflowEvent
|
|
200
|
+
await self.event_bus.publish(
|
|
201
|
+
WorkflowEvent(
|
|
202
|
+
event_type=EventType.WORKFLOW_STARTED,
|
|
203
|
+
workflow_id=workflow_id,
|
|
204
|
+
step_id=None,
|
|
205
|
+
data={},
|
|
206
|
+
timestamp=datetime.now(),
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
# Compare states to detect changes
|
|
212
|
+
old_state = old_state_data
|
|
213
|
+
|
|
214
|
+
# Check workflow status changes
|
|
215
|
+
if new_state.get("status") != old_state.get("status"):
|
|
216
|
+
if new_state.get("status") == "completed":
|
|
217
|
+
if self.on_status_change:
|
|
218
|
+
self.on_status_change(
|
|
219
|
+
StatusChange(
|
|
220
|
+
event_type=StatusChangeEvent.WORKFLOW_COMPLETED,
|
|
221
|
+
workflow_id=workflow_id,
|
|
222
|
+
timestamp=datetime.now(),
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
# Publish event to event bus (Phase 2)
|
|
226
|
+
if self.event_bus:
|
|
227
|
+
from .events import EventType, WorkflowEvent
|
|
228
|
+
await self.event_bus.publish(
|
|
229
|
+
WorkflowEvent(
|
|
230
|
+
event_type=EventType.WORKFLOW_COMPLETED,
|
|
231
|
+
workflow_id=workflow_id,
|
|
232
|
+
step_id=None,
|
|
233
|
+
data={},
|
|
234
|
+
timestamp=datetime.now(),
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
elif new_state.get("status") == "failed":
|
|
238
|
+
if self.on_status_change:
|
|
239
|
+
self.on_status_change(
|
|
240
|
+
StatusChange(
|
|
241
|
+
event_type=StatusChangeEvent.WORKFLOW_FAILED,
|
|
242
|
+
workflow_id=workflow_id,
|
|
243
|
+
timestamp=datetime.now(),
|
|
244
|
+
error=new_state.get("error"),
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
# Publish event to event bus (Phase 2)
|
|
248
|
+
if self.event_bus:
|
|
249
|
+
from .events import EventType, WorkflowEvent
|
|
250
|
+
await self.event_bus.publish(
|
|
251
|
+
WorkflowEvent(
|
|
252
|
+
event_type=EventType.WORKFLOW_FAILED,
|
|
253
|
+
workflow_id=workflow_id,
|
|
254
|
+
step_id=None,
|
|
255
|
+
data={"error": new_state.get("error")},
|
|
256
|
+
timestamp=datetime.now(),
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Check step execution changes
|
|
261
|
+
old_step_executions = {
|
|
262
|
+
se["step_id"]: se
|
|
263
|
+
for se in old_state.get("step_executions", [])
|
|
264
|
+
}
|
|
265
|
+
new_step_executions = {
|
|
266
|
+
se["step_id"]: se
|
|
267
|
+
for se in new_state.get("step_executions", [])
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Detect new step executions
|
|
271
|
+
for step_id, step_exec in new_step_executions.items():
|
|
272
|
+
old_step_exec = old_step_executions.get(step_id)
|
|
273
|
+
if not old_step_exec:
|
|
274
|
+
# New step started
|
|
275
|
+
if self.on_status_change:
|
|
276
|
+
self.on_status_change(
|
|
277
|
+
StatusChange(
|
|
278
|
+
event_type=StatusChangeEvent.STEP_STARTED,
|
|
279
|
+
workflow_id=workflow_id,
|
|
280
|
+
timestamp=datetime.now(),
|
|
281
|
+
step_id=step_id,
|
|
282
|
+
agent=step_exec.get("agent"),
|
|
283
|
+
action=step_exec.get("action"),
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
# Publish event to event bus (Phase 2)
|
|
287
|
+
if self.event_bus:
|
|
288
|
+
from .events import EventType, WorkflowEvent
|
|
289
|
+
await self.event_bus.publish(
|
|
290
|
+
WorkflowEvent(
|
|
291
|
+
event_type=EventType.STEP_STARTED,
|
|
292
|
+
workflow_id=workflow_id,
|
|
293
|
+
step_id=step_id,
|
|
294
|
+
data={
|
|
295
|
+
"agent": step_exec.get("agent"),
|
|
296
|
+
"action": step_exec.get("action"),
|
|
297
|
+
},
|
|
298
|
+
timestamp=datetime.now(),
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
elif step_exec.get("status") != old_step_exec.get("status"):
|
|
302
|
+
# Step status changed
|
|
303
|
+
if step_exec.get("status") == "completed":
|
|
304
|
+
if self.on_status_change:
|
|
305
|
+
self.on_status_change(
|
|
306
|
+
StatusChange(
|
|
307
|
+
event_type=StatusChangeEvent.STEP_COMPLETED,
|
|
308
|
+
workflow_id=workflow_id,
|
|
309
|
+
timestamp=datetime.now(),
|
|
310
|
+
step_id=step_id,
|
|
311
|
+
agent=step_exec.get("agent"),
|
|
312
|
+
action=step_exec.get("action"),
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
# Publish event to event bus (Phase 2)
|
|
316
|
+
if self.event_bus:
|
|
317
|
+
from .events import EventType, WorkflowEvent
|
|
318
|
+
await self.event_bus.publish(
|
|
319
|
+
WorkflowEvent(
|
|
320
|
+
event_type=EventType.STEP_COMPLETED,
|
|
321
|
+
workflow_id=workflow_id,
|
|
322
|
+
step_id=step_id,
|
|
323
|
+
data={
|
|
324
|
+
"agent": step_exec.get("agent"),
|
|
325
|
+
"action": step_exec.get("action"),
|
|
326
|
+
},
|
|
327
|
+
timestamp=datetime.now(),
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
elif step_exec.get("status") == "failed":
|
|
331
|
+
if self.on_status_change:
|
|
332
|
+
self.on_status_change(
|
|
333
|
+
StatusChange(
|
|
334
|
+
event_type=StatusChangeEvent.STEP_FAILED,
|
|
335
|
+
workflow_id=workflow_id,
|
|
336
|
+
timestamp=datetime.now(),
|
|
337
|
+
step_id=step_id,
|
|
338
|
+
agent=step_exec.get("agent"),
|
|
339
|
+
action=step_exec.get("action"),
|
|
340
|
+
error=step_exec.get("error"),
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
# Publish event to event bus (Phase 2)
|
|
344
|
+
if self.event_bus:
|
|
345
|
+
from .events import EventType, WorkflowEvent
|
|
346
|
+
await self.event_bus.publish(
|
|
347
|
+
WorkflowEvent(
|
|
348
|
+
event_type=EventType.STEP_FAILED,
|
|
349
|
+
workflow_id=workflow_id,
|
|
350
|
+
step_id=step_id,
|
|
351
|
+
data={
|
|
352
|
+
"agent": step_exec.get("agent"),
|
|
353
|
+
"action": step_exec.get("action"),
|
|
354
|
+
"error": step_exec.get("error"),
|
|
355
|
+
},
|
|
356
|
+
timestamp=datetime.now(),
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
except Exception as e:
|
|
361
|
+
logger.error(f"Error detecting changes for {workflow_id}: {e}")
|
|
362
|
+
|
|
363
|
+
def _load_state(self, state_file: Path) -> dict[str, Any] | None:
|
|
364
|
+
"""
|
|
365
|
+
Load workflow state from file with safe loading and validation.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
state_file: Path to state file
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
State dictionary or None if failed
|
|
372
|
+
"""
|
|
373
|
+
from .file_utils import safe_load_json, is_file_stable
|
|
374
|
+
|
|
375
|
+
# Skip files that are too new (likely still being written)
|
|
376
|
+
if not is_file_stable(state_file, min_age_seconds=2.0):
|
|
377
|
+
return None
|
|
378
|
+
|
|
379
|
+
# Use safe loading with retry logic
|
|
380
|
+
state = safe_load_json(
|
|
381
|
+
state_file,
|
|
382
|
+
retries=2, # Fewer retries for monitoring (we'll check again next poll)
|
|
383
|
+
backoff=0.5,
|
|
384
|
+
min_age_seconds=2.0,
|
|
385
|
+
min_size=100, # Minimum reasonable state file size
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if state is None:
|
|
389
|
+
# Only log as debug to avoid spam - incomplete files are expected during writes
|
|
390
|
+
logger.debug(f"Could not load state file {state_file} (may be incomplete)")
|
|
391
|
+
|
|
392
|
+
return state
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class BackgroundAgentMonitor:
|
|
396
|
+
"""Monitors Background Agent execution status."""
|
|
397
|
+
|
|
398
|
+
def __init__(
|
|
399
|
+
self,
|
|
400
|
+
worktree_base: Path,
|
|
401
|
+
on_status_change: Callable[[StatusChange], None] | None = None,
|
|
402
|
+
):
|
|
403
|
+
"""
|
|
404
|
+
Initialize Background Agent monitor.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
worktree_base: Base directory for worktrees
|
|
408
|
+
on_status_change: Callback when status changes detected
|
|
409
|
+
"""
|
|
410
|
+
self.worktree_base = Path(worktree_base)
|
|
411
|
+
self.on_status_change = on_status_change
|
|
412
|
+
|
|
413
|
+
async def monitor_worktree(
|
|
414
|
+
self, worktree_path: Path, workflow_id: str, step_id: str
|
|
415
|
+
) -> None:
|
|
416
|
+
"""
|
|
417
|
+
Monitor a specific worktree for Background Agent completion.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
worktree_path: Path to worktree
|
|
421
|
+
workflow_id: Workflow ID
|
|
422
|
+
step_id: Step ID
|
|
423
|
+
"""
|
|
424
|
+
# Check for progress files or completion markers
|
|
425
|
+
progress_file = worktree_path / ".tapps-agents" / "progress.json"
|
|
426
|
+
completion_file = worktree_path / ".tapps-agents" / "completed.txt"
|
|
427
|
+
|
|
428
|
+
if completion_file.exists():
|
|
429
|
+
if self.on_status_change:
|
|
430
|
+
self.on_status_change(
|
|
431
|
+
StatusChange(
|
|
432
|
+
event_type=StatusChangeEvent.STEP_COMPLETED,
|
|
433
|
+
workflow_id=workflow_id,
|
|
434
|
+
timestamp=datetime.now(),
|
|
435
|
+
step_id=step_id,
|
|
436
|
+
)
|
|
437
|
+
)
|
|
438
|
+
elif progress_file.exists():
|
|
439
|
+
# Background Agent is running, could emit progress events
|
|
440
|
+
if self.on_status_change:
|
|
441
|
+
self.on_status_change(
|
|
442
|
+
StatusChange(
|
|
443
|
+
event_type=StatusChangeEvent.STEP_PROGRESS,
|
|
444
|
+
workflow_id=workflow_id,
|
|
445
|
+
timestamp=datetime.now(),
|
|
446
|
+
step_id=step_id,
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
|