# Project export: Detour

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: On-board AI agents autonomously saving satellites from orbital debris.
- Devpost: https://devpost.com/software/detour-64kpds
- GitHub: https://github.com/keanucz/detour
- Demo: https://detour-azure.vercel.app/
- Video: https://www.youtube.com/embed/XTLts_t78IQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner ([NVIDIA] Edge AI Track)
- Team: 5 GitHub contributor(s) — Keanu Czirjak (33 commits), justyna-przy (24 commits), Adit Magotra (12 commits), Claude Opus 4.6 (8 commits), Ethan Lai (6 commits)

## Devpost submission (written by the team)

### Overview

Try it here -> https://detour-azure.vercel.app/ TL;DR 🛰️ Detour is an edge-first, autonomous decision-support system for satellite debris avoidance. Built for the ASUS Ascent GX10 / NVIDIA DGX Spark, Detour transforms raw orbital data into direct physical action, enabling agents to independently determine when a satellite requires a maneuver and generating the precise path-adjustment commands to execute it. It ingests live Two-Line Element (TLE) sets, propagates orbits in real-time, and generates a prioritized feed of Conjunction Data Message (CDM)-style events. The core of Detour is a multi-agent workflow powered by a local NVIDIA open model via vLLM. The system evaluates risks against real-world satellite constraints, such as fuel budgets, burn limits, and mission horizons, to ensure any proposed maneuver is both physically possible and operationally safe. Our agentic approach provides a transparent audit log, showing the system's reasoning as it screens, ranks, and triages the most dangerous objects in the debris field. Ultimately, Detour automates a high-stakes workflow that satellite operators have managed manually for decades, redefining the future of orbital infrastructure! The problem with today's debris avoidance Low Earth Orbit is getting overwhelmed with operational satellites and debris. Even a small debris fragment can cause catastrophic damage at orbital speeds. Satellite operators therefore run continuous conjunction assessments to predict close approaches and decide whether an avoidance maneuver is worth the operational cost (fuel/Δv, mission disruption, and the risk of creating new conjunctions). Conjunction warnings are typically delivered to operators as Conjunction Data Messages (CDMs), which are standardized notifications generated from tracking and screening systems that summarize an upcoming close approach. Operators are often overwhelmed by a constant stream of these messages. Because the data is so technically dense, it’s difficult to quickly filter out the noise and identify which threats actually require a maneuver.

### How we built it

The Agentic Engine Detour is designed to run entirely at the edge, simulating the air-gapped environment of a real satellite or ground station. We utilized the ASUS Ascent GX10 (NVIDIA DGX Spark) to host our agentic workforce, deploying the NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4 model via vLLM for low-latency reasoning. We split the workload into specialized roles: The Ingest & Screening Agents: These manage the live TLE data stream and run the heavy SGP4 physics to predict where every object in the catalog will be over the next 24 hours. The Navigator Agent: This agent acts as the primary "operator." It evaluates threats against the user-defined constraints and decides if a maneuver is necessary. -The Logic Loop: To ensure accuracy, we kept the math and logic separate. The agents call Python-based physics libraries for orbital propagation and only use the LLM layer for high-level decision logic and maneuver triage. The dashboard We built a Next.js + React + TypeScript dashboard styled with shadcn/ui for a clean, high-contrast mission-control layout. The visualization uses a Three.js 3D globe to render the target orbit and surrounding debris environment in real-time. Left Panel: Handles NORAD lookup and the live conjunction feed. Right Panel: Exposes constraint inputs that define the rules for the agents. Terminal: Provides a readable system trace, showing exactly how the agents are fetching data, screening threats, and calculating maneuvers.

### Challenges we ran into

We quickly found that LLMs are excellent at reasoning but terrible at orbital mechanics. Early versions of the agent tried to "guess" the result of a burn. We solved this by implementing a strict Tool Use architecture, forcing the model to offload all math to the SGP4 propagator and only interpret the results. We also found that our agents could not calculate all the orbital paths of the thousands of debris with a latency sufficient for inference; to fix this, we ended up using a Gaussian stochastic sampling distribution. to model each asteroid path. Lastly, running a local LLM alongside a heavy 3D visualization and real-time physics engine on a single edge device was a huge challenge. We had to aggressively optimize our vLLM configuration and context window to ensure the agents could act without choking the GPU.

### What we learned

We discovered that for time-sensitive orbital operations, latency is a safety risk. We learned how to optimize model throughput using vLLM to ensure that our Planning Agent could evaluate multiple maneuver candidates in seconds, which would be impossible with the round-trip latency of a cloud API. We learned that in the NVIDIA ecosystem, balancing between model size and hardware limits is really important. We spent a significant amount of time testing different quantizations and parameters to ensure our agents could run in parallel on the ASUS GX10 without hitting VRAM bottlenecks.

### What's next

⏭️ Onboard to operator-grade data sources: Integrate services that provide richer conjunction products (e.g., true CDMs, higher-fidelity screening outputs, and more frequent updates) so Detour’s agents can rank threats more accurately, reduce false alarms, and recommend safer maneuvers with clearer tradeoffs. Deploying to a real satellite :)

## README (from the GitHub repository)

# Detour — On-Board AI Agents Saving Satellites from Orbital Debris

**TreeHacks 2026 | NVIDIA Edge AI Track** — Honourable Mention Winner

**Devpost**
https://devpost.com/software/detour-64kpds?ref_content=user-portfolio&ref_feature=in_progress


Detour is an autonomous collision-avoidance system that runs **on-board** a satellite using NVIDIA's Nemotron LLM on the ASUS Ascent GX10 (Grace Blackwell). A multi-agent LangGraph pipeline detects debris threats, assesses risk, plans maneuvers, validates safety constraints, and executes avoidance burns — all locally with zero ground-station latency.

## Architecture

```
┌──────────────────────────────────────────────────────────────────┐
│                    ASUS Ascent GX10 (On-Board)                   │
│                                                                  │
│  ┌─────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐  ┌──────┐ │
│  │  SCOUT  │→ │ ANALYST  │→ │ PLANNER  │→ │ SAFETY │→ │ OPS  │ │
│  │ scan &  │  │ risk &   │  │ maneuver │  │ verify │  │BRIEF │ │
│  │ triage  │  │ refine   │  │ design   │  │& exec  │  │      │ │
│  └─────────┘  └──────────┘  └──────────┘  └────────┘  └──────┘ │
│       ↕             ↕             ↕             ↕               │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │              Physics Engine (deterministic)               │   │
│  │  screening · risk · CW dynamics · RK4 · SGP4 · Chan Pc   │   │
│  └──────────────────────────────────────────────────────────────┘   │
│       ↕                                                         │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │          Satellite Model (fuel, power, dynamics)          │   │
│  └──────────────────────────────────────────────────────────────┘   │
│       ↕                                                         │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  Nemotron 3 Nano 30B (NVFP4) via vLLM — local inference  │   │
│  └──────────────────────────────────────────────────────────────┘   │
└──────────────────────────────────────────────────────────────────┘
```

## Key Components

| Component | Path | Description |
|-----------|------|-------------|
| **Agent Pipeline** | `agents/` | LangGraph 5-agent pipeline with tool-calling |
| **Physics Engine** | `engine/` | RK4 solver, J2 perturbation, CW dynamics, Chan collision probability |
| **Satellite Model** | `engine/models/active_satellite.py` | Full orbital dynamics with resource management (fuel, power, battery) |
| **Tool Wrappers** | `agents/tools.py` | 11 LangChain tools wrapping the physics engine |
| **API** | `api/` | FastAPI server with agent, catalog, conjunction, and satellite endpoints |
| **Frontend** | `frontend/` | Next.js + React Three Fiber 3D globe with live satellite tracking |
| **Ascent GX10 Setup** | `scripts/setup_gx10.sh` | One-command setup for the ASUS Ascent GX10 |

## Agent Pipeline

| Agent | Role | Tools |
|-------|------|-------|
| **Scout** | Scan catalog for upcoming conjunctions, triage by severity | `scan_conjunctions`, `scan_demo_conjunctions` |
| **Analyst** | Deep risk assessment — Chan probability, high-fidelity TCA refinement | `assess_risk`, `refine_conjunction`, `propagate_orbit` |
| **Planner** | Design avoidance maneuvers considering satellite resources | `propose_avoidance_maneuvers`, `simulate_maneuver`, `get_satellite_status`, `check_maneuver_feasibility` |
| **Safety** | Validate constraints, approve or reject, execute approved burns | `check_maneuver_constraints`, `get_satellite_status`, `check_maneuver_feasibility`, `execute_maneuver_on_satellite` |
| **Ops Brief** | Generate human-readable summary for operators | _(synthesis only)_ |

## Quick Start

### 1. Backend

```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn api.app:app --reload --port 8000
```

### 2. Frontend

```bash
cd frontend
npm install
npm run dev  # localhost:3000
```

### 3. Agent System (with Ascent GX10)

```bash
# Start Nemotron on the Ascent GX10
chmod +x scripts/setup_gx10.sh
./scripts/setup_gx10.sh

# Run agent pipeline
python -m agents.run "Scan for conjunction threats to satellite 25544 in the next 48 hours" --demo
```

### 4. Agent System (without GPU — dev mode)

```bash
# Set OPENAI fallback in .env
NEMOTRON_BASE_URL=https://api.openai.com/v1
NEMOTRON_API_KEY=sk-...
NEMOTRON_MODEL=gpt-4o-mini

python -m agents.run "Scan for threats" --demo
```

## Model

**nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4** — 4-bit quantized (NVFP4) for fast edge inference on the Ascent GX10. ~15GB model weight footprint, leaving ample memory for KV cache and concurrent requests on the 128GB unified memory Grace Blackwell SoC.

Served locally via NGC vLLM container with tool-calling (`--enable-auto-tool-choice --tool-call-parser hermes --enable-chunked-prefill`).

## Why Edge AI?

| Ground Station | On-Board (Detour) |
|---------------|-------------------|
| 5-15 min communication delay | **< 1 sec** decision |
| Limited pass windows | **24/7** monitoring |
| Single point of failure | **Autonomous** operation |
| Manual operator in the loop | **Agent-validated** decisions |

In LEO, a debris collision can happen in minutes. You can't wait for the next ground station pass.

## Team

- **Justyna** — Frontend, 3D Visualization, UI/UX
- **Ethan** — ASUS Ascent GX10 Setup, Simulation Logic
- **Adit** — Satellite Data Feed, Simulation Logic
- **Keanu** — Ascent GX10 vLLM Setup, LangChain NVIDIA Nemotron Agent System


## Detected evidence (automated analysis)

Indexed codebase: 89 recognized source files, 409 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- LangChain (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (98 of 98)

```
.gitignore
agents/__init__.py
agents/config.py
agents/graph.py
agents/prompts.py
agents/run.py
agents/tools.py
api/__init__.py
api/app.py
api/demo_data.py
api/state.py
debris_ids.txt
engine/__init__.py
engine/config/__init__.py
engine/config/settings.py
engine/core/__init__.py
engine/core/engine1.py
engine/core/engine2.py
engine/core/risk_filter.py
engine/models/__init__.py
engine/models/active_satellite.py
engine/models/debris.py
engine/models/satellite.py
engine/physics/__init__.py
engine/physics/chan_probability.py
engine/physics/covariance.py
engine/physics/cw_relative.py
engine/physics/entity.py
engine/physics/ephemeris.py
engine/physics/forces.py
engine/physics/geometry.py
engine/physics/probability.py
engine/physics/solver_rk45.py
engine/physics/solver.py
engine/physics/state.py
engine/physics/third_body.py
engine/physics/utils.py
frontend/.eslintrc.json
frontend/.gitignore
frontend/app/api/active-threat/route.ts
frontend/app/api/constraints/route.ts
frontend/app/api/debris/route.ts
frontend/app/api/feed/route.ts
frontend/app/api/manual-satellite-state/route.ts
frontend/app/api/manual-satellite/route.ts
frontend/app/api/manual/maneuver-from-state/route.ts
frontend/app/api/manual/trajectory/route.ts
frontend/app/api/orbit/route.ts
frontend/app/api/tle/route.ts
frontend/app/globals.css
frontend/app/layout.tsx
frontend/app/page.tsx
frontend/app/simulation/page.tsx
frontend/components.json
frontend/components/constraints-panel.tsx
frontend/components/dashboard-header.tsx
frontend/components/dashboard-shell.tsx
frontend/components/globe-view.tsx
frontend/components/left-panel-content.tsx
frontend/components/moving-satellite.tsx
frontend/components/side-panel.tsx
frontend/components/simulation-controls.tsx
frontend/components/simulation-overlay-v2.tsx
frontend/components/simulation-overlay.tsx
frontend/components/simulation-view.tsx
frontend/components/terminal-drawer.tsx
frontend/components/ui/badge.tsx
frontend/lib/geo.ts
frontend/lib/server/config.ts
frontend/lib/server/feed.ts
frontend/lib/server/manual-orbit.ts
frontend/lib/server/sgp4.ts
frontend/lib/server/state.ts
frontend/lib/server/tle.ts
frontend/lib/server/types.ts
frontend/lib/sim-engine.ts
frontend/lib/simulation-helpers.ts
frontend/lib/simulation-types.ts
frontend/lib/utils.ts
frontend/next-env.d.ts
frontend/next.config.ts
frontend/package.json
frontend/postcss.config.mjs
frontend/README.md
frontend/tsconfig.json
PIPELINE.md
README.md
requirements.txt
run.sh
SATELLITE_VISUALIZATION.md
scripts/setup_gx10.sh
tools/__init__.py
tools/constraints.py
tools/maneuver.py
tools/propagate.py
tools/refine.py
tools/risk.py
tools/screening.py
```

### Dependencies

- frontend/package.json: @react-three/drei@^10.7.7, @react-three/fiber@^9.5.0, @tailwindcss/postcss@^4.1.0, @types/node@^22.13.1, @types/react@^19.0.8, @types/react-dom@^19.0.3, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9.19.0, eslint-config-next@15.2.0, lucide-react@^0.475.0, next@^15.5.12, react@19.0.0, react-dom@19.0.0, satellite.js@^6.0.2, tailwind-merge@^2.6.0, tailwindcss@^4.1.0, three@^0.173.0, typescript@^5.7.3
- requirements.txt: fastapi@>=0.109, langchain@>=0.3, langchain-core@>=0.3, langchain-openai@>=0.3, langgraph@>=0.2, numpy@>=1.24, uvicorn[standard]@>=0.27

### Recent commits (newest first)

- Update README.md
- Fix formatting for Devpost link in README
- Update README with Devpost link
- Point AI backend to detour-ai.keanuc.net, agent backend to detour-backend.keanuc.net
- Proxy /api/agent/* to detour-backend, /chat/completion to detour LLM
- add agent pipeline.md thing
- Fix orbit propagation: subtle perturbation + smooth red transition
- Wire agent maneuvers to globe visualization with speed control
- fixes config!
- fix tools
- Merge branch 'master' of https://github.com/keanucz/detour into agent-terminal
- add langchain agents and backend physics engine and frontend agent terminal
- its done
- adjustyed things
- polished up cdm stuff
- updated risks
- Update Ethan's role in README.md
- Fix collision avoidance: distance-dependent repulsion force
- Realistic CPA-based path planning for collision avoidance simulation
- deleted engine and api

## Key source files (fetched from GitHub, selected and truncated for size)

### SATELLITE_VISUALIZATION.md

```markdown
# Moving Satellite Visualization - Setup Guide

## What Was Changed

### Backend (3 files modified/created)

1. **`api/routes/satellite_demo.py`** (NEW)
   - Creates an endpoint `/api/satellite-demo/iss-trajectory`
   - Uses your `Satellite` class from `engine/models/satellite.py`
   - Propagates the satellite orbit over time (90 minutes default)
   - Returns trajectory data: times, positions, velocities

2. **`api/app.py`** (MODIFIED)
   - Added import for `satellite_demo` router
   - Registered the new endpoint in the FastAPI app

### Frontend (2 files modified/created)

3. **`frontend2/components/moving-satellite.tsx`** (NEW)
   - React Three.js component that animates a satellite along its trajectory
   - Shows the satellite as a red sphere moving in real-time
   - Displays an orbital trail showing the full path

4. **`frontend2/components/globe-view.tsx`** (MODIFIED)
   - Fetches trajectory from the backend
   - Renders the `MovingSatellite` component
   - Now shows: Earth + Mock satellites + YOUR moving satellite (red)

---

## How to Run

### 1. Start the Backend

```bash
cd /Users/the_alphalaser/Desktop/Coding/detour

# Make sure dependencies are installed
pip install fastapi uvicorn numpy python-dotenv

# Start the API server
uvicorn api.app:app --reload --port 8000
```

The backend will be available at: http://localhost:8000

### 2. Start the Frontend

```bash
cd /Users/the_alphalaser/Desktop/Coding/detour/frontend2

# Install dependencies (if not already done)
npm install

# Start the Next.js dev server
npm run dev
```

The frontend will be available at: http://localhost:3000

### 3. View the Result

Open your browser to http://localhost:3000

You should see:
- Earth in the center (rotating slowly)
- Thousands of small blue dots (mock satellites)
- **ONE RED SATELLITE** moving along its orbital path - this is YOUR `Satellite` object!

---

## What You're Seeing

The red satellite is:
1. Created using your `Satellite` class with ISS-like initial conditions
2. Propagated using simple two-body dynamics (can be upgraded to your physics engine)
3. Animated in real-time along its orbital trajectory
4. Showing both the satellite position AND its full orbital trail

---

## Next Steps

### Make it More Realistic

Replace the simple propagation in `satellite_demo.py` with your existing tools:

```python
# Instead of _simple_propagate_step, use:
from tools.propagate import propagate_orbits

# Or use your more sophisticated physics:
from engine.physics.solver import propagate_with_perturbations
```

### Add More Satellites

Modify the endpoint to return multiple satellites:

```python
@router.get("/satellites")
async def get_multiple_satellites():
    satellites = [
        {"id": "ISS", "position": [...], "velocity": [...]},
        {"id": "HST", "position": [...], "velocity": [...]},
    ]
    # Return trajectories for all
```

### Show Covariance Uncertainty

Your `Satellite` class has `cov_pos` and `cov_vel`. You can visualize this as uncerta
[truncated — 1414 more characters]
```

### PIPELINE.md

```markdown
# Detour Agent Pipeline

Sequential 5-agent collision avoidance pipeline running on-board via LangGraph.

```
Scout → Analyst → Planner → Safety → Ops Brief → END
```

---

## Agent 0 — Scout

**Role:** Conjunction scanner — first line of defense.

**Tools:**
| Tool | Description |
|------|-------------|
| `get_pending_cdms` | Retrieve incoming Conjunction Data Messages |
| `scan_conjunctions` | Screen orbital catalog for close approaches |
| `scan_demo_conjunctions` | Load demo debris data and scan |

**Steps:**
1. `scan_demo_conjunctions` or `scan_conjunctions` to find close approaches
2. `get_pending_cdms` to check for pending CDMs
3. Summarize threats: total screened, top 5 ranked by miss distance

---

## Agent 1 — Analyst

**Role:** Deep risk assessment — collision probability and HiFi refinement.

**Tools:**
| Tool | Description |
|------|-------------|
| `get_pending_cdms` | Retrieve incoming CDMs |
| `scan_conjunctions` | Re-scan catalog if needed |
| `scan_demo_conjunctions` | Re-scan demo data if needed |
| `assess_risk` | Compute collision probability (Chan method) for an event |
| `refine_conjunction_hifi` | HiFi propagation (RK45 + J2/J3/J4 + drag) for TCA refinement |

**Steps:**
1. Review Scout findings
2. `assess_risk` for each flagged event → collision probability + risk level
3. `refine_conjunction_hifi` for critical/high events → refined TCA and miss distance
4. Rank by urgency, flag events needing maneuvers

---

## Agent 2 — Planner

**Role:** Trajectory optimization — designs avoidance maneuvers using CW dynamics.

**Tools:**
| Tool | Description |
|------|-------------|
| `propose_avoidance_maneuvers` | Generate candidate delta-V profiles (along-track, radial, cross-track) |
| `simulate_maneuver_effect` | Simulate a candidate to verify miss distance improvement |
| `assess_risk` | Re-assess risk with maneuver applied |

**Steps:**
1. `propose_avoidance_maneuvers` for each high-risk event
2. `simulate_maneuver_effect` for top 1-2 candidates
3. Select best maneuver: minimize fuel, maximize risk reduction
4. Output ranked candidates with delta-v, fuel cost, predicted miss distance

---

## Agent 3 — Safety

**Role:** Resource guardian — enforces constraints, protects satellite resources.

**Tools:**
| Tool | Description |
|------|-------------|
| `get_satellite_status` | Current telemetry: fuel, power, delta-v budget |
| `check_maneuver_constraints` | Validate fuel budget, max delta-v, min altitude, blackout windows |
| `check_maneuver_feasibility` | Verify maneuver is physically executable |
| `propagate_satellite_orbit` | Propagate trajectory to verify post-maneuver orbit |

**Steps:**
1. `get_satellite_status` → fuel %, power %, operational status
2. `check_maneuver_feasibility` for each proposed maneuver
3. `check_maneuver_constraints` → pass/fail per constraint
4. `propagate_satellite_orbit` to verify trajectory
5. Verdict: APPROVED / CONDITIONAL / REJECTED

---

## Agent 4 — Ops Brief

**Role:** Execution and operator b
[truncated — 1780 more characters]
```

### requirements.txt

```
numpy>=1.24
fastapi>=0.109
uvicorn[standard]>=0.27

# ── LangChain / LangGraph (Nemotron agent system) ──
langchain>=0.3
langchain-openai>=0.3
langchain-core>=0.3
langgraph>=0.2

```

### frontend/package.json

```
{
  "name": "frontend2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@react-three/drei": "^10.7.7",
    "@react-three/fiber": "^9.5.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.475.0",
    "next": "^15.5.12",
    "react": "19.0.0",
    "react-dom": "19.0.0",
    "satellite.js": "^6.0.2",
    "tailwind-merge": "^2.6.0",
    "three": "^0.173.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.1.0",
    "@types/node": "^22.13.1",
    "@types/react": "^19.0.8",
    "@types/react-dom": "^19.0.3",
    "eslint": "^9.19.0",
    "eslint-config-next": "15.2.0",
    "tailwindcss": "^4.1.0",
    "typescript": "^5.7.3"
  }
}

```

### api/app.py

```python
"""
FastAPI backend for the Detour agent system.

Provides SSE streaming of agent pipeline events to the frontend.
The frontend's physics/visualization stays in TypeScript — this API
only exposes the LLM agent pipeline for the terminal drawer.
"""
from __future__ import annotations

import asyncio
import json
import logging
import os
import time
from contextlib import asynccontextmanager
from typing import Optional

from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse

from agents.config import LLMConfig
from agents.graph import run_avoidance_pipeline, stream_avoidance_pipeline
from api.demo_data import load_demo_data
from api.state import get_satellite, reset_state

logger = logging.getLogger("detour.api")
logging.basicConfig(level=logging.INFO)


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Load demo data on startup."""
    logger.info("Loading demo data...")
    load_demo_data()
    logger.info("Demo data loaded. Agent API ready.")
    yield


app = FastAPI(
    title="Detour Agent API",
    description="Internal API for the Detour collision avoidance agent system",
    version="0.1.0",
    lifespan=lifespan,
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


def _get_config() -> LLMConfig:
    """Build LLM config from environment."""
    return LLMConfig.from_env()


# ─────────────────────────────────────────────────────────────────────────
# Health / status
# ─────────────────────────────────────────────────────────────────────────

@app.get("/health")
def health():
    return {"status": "ok", "service": "detour-agent-api"}


@app.get("/status")
def status():
    sat = get_satellite()
    config = _get_config()
    return {
        "satellite": {
            "norad_id": sat.norad_id,
            "name": sat.name,
            "fuel_pct": round(sat.fuel_kg / sat.config.fuel_capacity * 100, 1),
        },
        "llm": {
            "model": config.model,
            "base_url": config.base_url,
        },
    }


# ─────────────────────────────────────────────────────────────────────────
# Agent pipeline — synchronous (returns full result)
# ─────────────────────────────────────────────────────────────────────────

@app.post("/agent/run")
def agent_run(
    prompt: str = "Scan for conjunction threats against the ISS in the next 24 hours. If any are high risk, propose avoidance maneuvers and check constraints. Use the demo dataset.",
    mode: str = "multi",
):
    """
    Run the agent pipeline synchronously and return the full result.
    Use /agent/stream for real-time SSE updates.
    """
    config = _get_config()
    result = run_avoidance_pipeline(prompt, config=config, mode=mode)
    return result


# ─────────────────────────────────────────────────────────────────────────
# Agent pipeline — SSE streaming (real-time events for terminal drawer)
# ─────────────────────────────────────────────────────────────────────────

@app.get("/agent/stream")
async def agent_stream(
    prompt: str = Query(
        default="Scan for conjunction threats against the ISS in the next 24 hours. If any are high risk, propose avoidance maneuvers and check constraints. Use the demo dataset.",
        description="Natural language request for the agent",
    ),
    mode: str = Query(default="multi", description="Agent mode: multi or single"),
):
    """
    Stream agent pipeline events via Server-Sent Events (SSE).
    The frontend terminal drawer connects to this endpoint.
    """
    config = _get_config()

    async def event_generator():
        try:
            async for event in stream_avoidance_pipeline(prompt, config=config, mode=mode):
                data = json.dumps(event, default=str)
                yield f"data: {data}\n\n"
        except Exception as e:
            yield f"data: {json.dumps({'type': 'error', 'message': str(e)})}\n\n"
        finally:
            yield f"data: {json.dumps({'type': 'done', 'timestamp': time.time()})}\n\n"

    return StreamingResponse(
        event_generator(),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )


# ─────────────────────────────────────────────────────────────────────────
# Demo data management
# ─────────────────────────────────────────────────────────────────────────

@app.post("/demo/reload")
def demo_reload():
    """Reset state and reload demo data."""
    reset_state()
    summary = load_demo_data()
    return {"ok": True, "summary": summary}

```

### frontend/app/page.tsx

```typescript
import { DashboardShell } from "@/components/dashboard-shell"

export default function HomePage() {
  return <DashboardShell />
}

```

### frontend/app/layout.tsx

```typescript
import type { Metadata } from "next"

import "./globals.css"

export const metadata: Metadata = {
  title: "Detour",
  description: "Autonomous debris avoidance dashboard",
  icons: {
    icon: "/favicon.png",
  },
}

export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
  return (
    <html lang="en">
      <body suppressHydrationWarning>{children}</body>
    </html>
  )
}

```

### frontend/app/simulation/page.tsx

```typescript
import { SimulationView } from "@/components/simulation-view"

export default function SimulationPage() {
  return <SimulationView />
}

```

### frontend/app/api/manual-satellite-state/route.ts

```typescript
import { NextResponse } from "next/server"
import { getManualSatellite } from "@/lib/server/state"

export const runtime = "nodejs"

export async function GET() {
  const manualSat = getManualSatellite()

  if (!manualSat) {
    return NextResponse.json({ error: "No manual satellite loaded" }, { status: 404 })
  }

  return NextResponse.json({
    position: manualSat.position,
    velocity: manualSat.velocity,
    epoch: manualSat.epoch,
  })
}

```

### frontend/app/api/manual-satellite/route.ts

```typescript
import { NextRequest, NextResponse } from "next/server"
import { setManualSatellite } from "@/lib/server/state"

export const runtime = "nodejs"

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()

    if (!body.position || !body.velocity || !body.epoch || !body.trajectory) {
      return NextResponse.json({ error: "Missing state vector or trajectory data" }, { status: 400 })
    }

    const epochDate = new Date(body.epoch)
    setManualSatellite({
      position: body.position,
      velocity: body.velocity,
      epoch: body.epoch,
      epochMs: epochDate.getTime(),
      trajectory: body.trajectory,
    })

    return NextResponse.json({ ok: true })
  } catch (error) {
    return NextResponse.json(
      { error: "Failed to store manual satellite", detail: error instanceof Error ? error.message : "Unknown error" },
      { status: 500 }
    )
  }
}

export async function DELETE() {
  setManualSatellite(null)
  return NextResponse.json({ ok: true })
}

```

[74 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]