WebSocket endpoint for project updates. Streams: - Progress updates (passing/total counts) - Agent status changes - Agent stdout/stderr lines
(websocket: WebSocket, project_name: str)
| 719 | |
| 720 | |
| 721 | async def project_websocket(websocket: WebSocket, project_name: str): |
| 722 | """ |
| 723 | WebSocket endpoint for project updates. |
| 724 | |
| 725 | Streams: |
| 726 | - Progress updates (passing/total counts) |
| 727 | - Agent status changes |
| 728 | - Agent stdout/stderr lines |
| 729 | """ |
| 730 | if not validate_project_name(project_name): |
| 731 | await websocket.close(code=4000, reason="Invalid project name") |
| 732 | return |
| 733 | |
| 734 | project_dir = _get_project_path(project_name) |
| 735 | if not project_dir: |
| 736 | await websocket.close(code=4004, reason="Project not found in registry") |
| 737 | return |
| 738 | |
| 739 | if not project_dir.exists(): |
| 740 | await websocket.close(code=4004, reason="Project directory not found") |
| 741 | return |
| 742 | |
| 743 | await manager.connect(websocket, project_name) |
| 744 | |
| 745 | # Get agent manager and register callbacks |
| 746 | agent_manager = get_manager(project_name, project_dir, ROOT_DIR) |
| 747 | |
| 748 | # Create agent tracker for multi-agent mode |
| 749 | agent_tracker = AgentTracker() |
| 750 | |
| 751 | # Create orchestrator tracker for observability |
| 752 | orchestrator_tracker = OrchestratorTracker() |
| 753 | |
| 754 | async def on_output(line: str): |
| 755 | """Handle agent output - broadcast to this WebSocket.""" |
| 756 | try: |
| 757 | # Extract feature ID from line if present |
| 758 | feature_id = None |
| 759 | agent_index = None |
| 760 | match = FEATURE_ID_PATTERN.match(line) |
| 761 | if match: |
| 762 | feature_id = int(match.group(1)) |
| 763 | agent_index, _ = await agent_tracker.get_agent_info(feature_id) |
| 764 | |
| 765 | # Send the raw log line with optional feature/agent attribution |
| 766 | log_msg: dict[str, str | int] = { |
| 767 | "type": "log", |
| 768 | "line": line, |
| 769 | "timestamp": datetime.now().isoformat(), |
| 770 | } |
| 771 | if feature_id is not None: |
| 772 | log_msg["featureId"] = feature_id |
| 773 | if agent_index is not None: |
| 774 | log_msg["agentIndex"] = agent_index |
| 775 | |
| 776 | await websocket.send_json(log_msg) |
| 777 | |
| 778 | # Check if this line indicates agent activity (parallel mode) |
no test coverage detected