# Project export: CrisisRoom

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: UC Berkeley AI Hackathon 2026
- Tagline: Built by a Navy damage controlman who fought fire and flooding at sea. When every second counts, turn disaster chaos into a clear action plan, powered by an AI agent swarm.
- Devpost: https://devpost.com/software/crisisroom
- GitHub: https://github.com/uc-berkeley-ai-hackathon-2026/crisis-room
- Team: 4 GitHub contributor(s) — Joshua N. (32 commits), NatalieeeeeYan (9 commits), Cursor (7 commits), Claude Sonnet 4.6 (1 commits)

## Devpost submission (written by the team)

### Inspiration

I served in the Navy. One of my roles was damage control: keeping the ship alive when fire and flooding hit at the same time. Let me tell you what that actually feels like. You are in a steel passageway. The lights are strobing. Smoke is filling the space faster than you can breathe through it, water is rising past your boots, and a general alarm is screaming so loud you cannot hear the person next to you. Somewhere on that ship is a sailor's son, a sister, somebody's whole world, and the only thing standing between them and the sea is whether the people on the bridge can understand what is happening fast enough to act. And here is the brutal truth I lived: the enemy was never a lack of information. It was the flood of it. Bilge alarms, thermal sensors, smoke detectors, and frantic radio reports all screaming at once, while we tracked the fight for a billion-dollar warship and the lives aboard it on a grease pencil and a plexiglass board. One shared board. One fragile source of truth. Every update erased the last one. On a calm day with a clean log, roughly 1% of hand-entered data is wrong. Now add smoke, heat, and adrenaline. The studies are blunt about where this leads: $$P(\text{accident} \mid \text{human error}) \approx 0.75\text{ to }0.96 \quad \text{(Allianz AGCS Safety & Shipping Review)}$$ Three out of four marine disasters trace back not to broken steel, but to a human being who was handed too much noise and too little time. I left the Navy. I never left that feeling. CrisisRoom is the second brain I wish we'd had on the deck.

### What it does

CrisisRoom turns crisis chaos into one clear, verified plan by running a live incident through a swarm of specialist AI agents that think together, the way a trained damage-control team does: Hazard Intelligence: ranks flood, fire, and smoke severity and localizes each threat to a specific compartment. Damage Control: assesses watertight integrity, bilge pumping capacity, fire suppression readiness, and structural risk, then recommends containment actions. Response Coordination: turns confirmed hazards and containment status into prioritized, sequenced response tasks with owner roles. Orchestrator: threads each agent's output into the next and synthesizes a single bridge brief a human can act on in seconds. Integrations Fetch AI: Four collaborating uAgents (Orchestrator, Hazard, Response, Advisory) run in a Bureau with real agent-to-agent messaging, powered by ASI:One and registered on Agentverse. The agent fleet + ASI:One LLM drive the entire crisis pipeline. Redis: Hits all three pillars: Redis for agent long-term memory, vector search (RediSearch KNN), and context retrieval (Streams + hybrid search). Redis is the agents' brain, not a cache. Arize: OpenInference tracing on every LLM call plus an LLM-as-judge scoring safety_policy/groundedness that surfaced and let us fix unsafe outputs. Observability that measurably improved the app. (https://app.arize.com/organizations/QWNjb3VudE9yZ2FuaXphdGlvbjo0NDc0NTptcFpJ/spaces/U3BhY2U6NDcyOTg6MVJjRg==/projects/TW9kZWw6ODQ3Mjg4MDQwMzo3bU1F?traceViewId=__arize_default&timeZoneA=America%2FLos_Angeles&selectedTab=llmTracing&startA=1781420400000&endA=1782039599999&envA=tracing&timeRangeKeyA=custom&modelType=generative_llm) Deepgram: Nova-3 STT + Aura-2 TTS as a bidirectional, hands-free crisis voice dispatcher. Voice is the core hands-free command interface, not tacked on. Orkes: AgentSpan durably generates the brief's top_risks and recommended_actions via two chained agent executions. Durable risk/action planning inside the live pipeline. The Token Company: Built CRISP, a reversible, query-aware context-compression framework plus an agentic compressor, with research docs and benchmarks. Novel research cutting LLM context cost. (https://github.com/uc-berkeley-ai-hackathon-2026/crisis-room/blob/main/docs/crisp-technical-deck.html) Sentry: Live sentry_sdk with PII scrubbing and incident-tagged error capture across the FastAPI backend and Next.js frontend. Error monitoring from day one. (https://carnegie-mellon-university-d3.sentry.io/issues/?project=4511600565420032) Anthropic: Built the whole project with Claude Code, tackling emergency response — a high-stakes "shift what's possible for people" domain. Claude Code as one of the primary development tool. Midjourney: 8 cinematic deck backgrounds plus the in-app vessel hazard schematic (HazardMap.tsx, labeled "Midjourney render"). Generated imagery for both product UI and pitch. (https://github.com/uc-berkeley-ai-hackathon-2026/crisis-room/blob/main/apps/web/public/vessel-schematic.png) Cognition (Devin): Used Devin as an AI coding agent during the build, especially via the cloud. AI-assisted coding combined with Claude Code.

### How we built it

Frontend and backend: a Next.js dashboard and a typed FastAPI service over REST and WebSocket. It runs mock-first with zero API keys, then lights up as each integration connects. A judge can clone it and watch it work in one command. Agents: four Fetch.ai uAgents in a single Bureau, discoverable on Agentverse and collaborating in-process, with ASI:One as the reasoning LLM behind every one of them. Memory: Redis as the primary store, not a cache, in three tiers: Redis Iris for long-term semantic memory, RedisVL vector search (HNSW, cosine) for the live session, and a lexical fallback. Streams carry real-time coordination events. Durable planning: Orkes AgentSpan runs risk_assessor then action_planner as chained, durable executions, so the highest-stakes part of the brief is orchestrated and observable. Observability and safety: every LLM call is traced to Arize, and an LLM-as-judge scores each output on safety_policy and groundedness, writing the verdict back onto the exact span. CRISP: a reversible, query-aware compression framework we built and benchmarked to shrink crisis context while protecting the numbers and entities that decide whether someone lives.

### Challenges we ran into

Cross-process uAgent messaging needs the Fetch.ai Almanac network, which was unreachable for us. We solved it with an in-process Bureau that still registers on Agentverse for discoverability. Early prompts occasionally implied an alert "had been sent." In a real crisis, that single false sentence kills people. Our Arize safety evaluator caught it, and we drove the failure rate down until it was gone. Failed durable executions leaked raw error text into the brief. We fixed the parser to fail safe and fall back cleanly, because a wrong answer dressed as a confident one is the most dangerous output a crisis tool can produce. We made Redis, ASI:One, RediSearch, and voice all optional with deterministic fallbacks, because the moment you need this system most is the moment the network is on fire too.

### Accomplishments we're proud of

A genuinely collaborating agent swarm, not four disconnected prompts wearing a trench coat. Redis used as an AI-native brain, with Streams and vector search, holding sub-20ms latency at scale. Safety we can actually measure, through an LLM-as-judge wired onto live traces, instead of hoping the model behaved. A system that runs anywhere, with zero keys, in one command, so the people who need it are never blocked by a missing credential. Most of all: we took the worst day of my old job and built something so the next damage controlman has a fighting chance the rest of us did not.

### What we learned

Observability is a safety feature, not an afterthought. If you cannot measure whether your AI is safe, it is not. Redis is an AI-native database, not just a cache. Human-in-the-loop is not a limitation. It is the product. Trust is the feature people are actually buying. Build the fallbacks first. In a real crisis, the happy path is the first thing to burn.

## README (from the GitHub repository)

# FloodFire CrisisRoom

FloodFire CrisisRoom is a multi-agent shipboard emergency command dashboard that ingests flooding, fire, smoke, and crew-report signals, verifies them, creates response tasks, and drafts crew/passenger advisories for bridge approval.

This is a hackathon MVP scaffold. It is mock-first, typed, and runnable without external API keys.

## 🏆 Redis Beyond Caching - Prize Track

**Redis is the intelligent memory layer** powering multi-agent crisis coordination:

- ✅ **Vector Search**: Semantic memory retrieval (RediSearch + embeddings)
- ✅ **Real-Time Streams**: Agent coordination via Redis Streams
- ✅ **AI Memory**: Persistent agent context across sessions
- ✅ **Hybrid Search**: Keyword + vector similarity fusion

**Not a cache** - Redis is the primary data store for agent memories, event sourcing, and semantic search.

📚 **See:** [README_REDIS.md](./README_REDIS.md) | [REDIS_SHOWCASE.md](./REDIS_SHOWCASE.md) | [REDIS_ARCHITECTURE.md](./REDIS_ARCHITECTURE.md)

## Hackathon Prize-Track Strategy

- **AI agents:** clear agent boundaries for Commander, Flood Analyst, Fire Analyst, Verification, Response Planner, and Public Information.
- **Real-time data:** connector stubs and simulated shipboard sensors for bilge alarms, pump-room water rise, Engine Room thermal alarms, smoke reports, and crew field reports.
- **Voice:** Deepgram-ready voice report interface with manual fallback.
- **Memory and coordination:** Redis placeholder for incident memory, dedupe, and agent state.
- **Redis beyond caching:** Redis stores durable crisis memory, stream-based operational events, and agent context retrieval. If Redis Stack/RediSearch is available, the app reports vector-search readiness; otherwise it uses lexical retrieval over Redis memory hashes.
- **Observability:** Arize and Sentry placeholders for tracing, evaluation, and reliability.
- **Human-centered safety:** crew/passenger advisories are drafts only and require bridge/human approval.

## Architecture

```text
apps/web Next.js dashboard
  -> typed API client
  -> FastAPI REST + WebSocket
  -> route modules
  -> services: simulation, verification, agents, tasks, alert drafting
  -> in-memory store by default
  -> optional Redis and external connector stubs
```

## Setup

```bash
cp .env.example .env
```

Put real secrets only in `.env`. The committed `.env.example` intentionally contains placeholders.

Minimum keys for the full demo:

- `ASI_ONE_API_KEY` — Fetch.ai ASI:One LLM
- `AGENTVERSE_API_KEY` — Agentverse mailbox for discoverable uAgents

Optional keys:

- `ASI_ONE_MODEL`: ASI:One model name, defaults to `asi1-mini`.
- `ANTHROPIC_API_KEY`: future Claude agent reasoning.
- `REDIS_URL`: local or hosted Redis connection string.
- `REDIS_ACCOUNT_KEY`, `REDIS_API_KEY`: hosted Redis account/API access if your Redis provider requires them.
- `DEEPGRAM_API_KEY`: voice transcription.
- `BROWSERBASE_API_KEY`: official-source browser checks.
- `ARIZE_API_KEY` + `ARIZE_SPACE_ID`: LLM tracing/evaluation (see **Arize Observability** below).
- `SENTRY_DSN`: frontend/backend error monitoring.
- `NASA_FIRMS_MAP_KEY`: real active fire detections.

## Quick Start

Run these from the **repo root** (`crisis-room/`).

### 1. One-time install

```bash
npm install
cd api && python3 -m venv .venv && . .venv/bin/activate && pip install -e .
```

### 2. Start the app (terminal 1)

```bash
npm run dev
```

Starts:

- **Frontend:** http://localhost:3000/dashboard
- **API:** http://localhost:8000

Leave this terminal running.

### 3. Start Fetch.ai agents (terminal 2, optional)

Required for the Agentverse sponsor track. The dashboard works without this if `ASI_ONE_API_KEY` is set — ASI:One is called directly from FastAPI.

```bash
npm run dev:agents
```

Starts all four agents in a single uAgents **Bureau** (one process, served on `:8100`).
They register on Agentverse (discoverable) **and** communicate with each other in-process
(see [Agent-to-agent communication](#agent-to-agent-communication)):

| Agent | Role |
|---|---|
| FloodFire Crisis Orchestrator | Coordinates the specialist swarm and synthesizes the bridge situation brief |
| Hazard Intelligence Agent | Assesses flood/fire/smoke indicators, localizes to compartments, ranks severity |
| Response Coordination Agent | Turns confirmed hazards into prioritized, sequenced response tasks |
| Public Advisory Agent | Drafts approval-gated crew/passenger advisories (never claims transmission) |

On startup it sends one demo incident through the pipeline and prints the assembled brief,
then stays running. Use `--no-demo` to skip the demo, or `--once` to exit after it. Each
agent's address is printed at startup. Leave this terminal running.

### 4. Start Redis (terminal 3, optional)

Only if you want Redis crisis memory persistence:

```bash
redis-server
```

Or point `REDIS_URL` in `.env` at a hosted Redis instance — no local Redis process needed.

### 5. Agentverse connectivity

The Bureau batch-registers all four agents on Agentverse automatically at startup
(`Batch registration on Almanac API successful`) — no manual connection step.

To run the agents as **separate processes** instead (each on its own port, e.g. for
individual Agentverse mailbox/proxy connection), use the single-agent runner — see
[Agent-to-agent communication](#agent-to-agent-communication). Standalone agents honor
`FETCH_CONNECTIVITY` (`proxy` default, or `mailbox`/`endpoint`).

### 6. Verify

```bash
curl http://localhost:8000/
curl http://localhost:8000/api/fetch/status
```

### Stop everything

```bash
for p in 3000 8000 8100; do lsof -ti:$p | xargs kill 2>/dev/null; done
```

## Run scripts reference

| Command | What it does |
|---|---|
| `npm run dev` | Frontend + API together (recommended) |
| `npm run dev:web` | Next.js only on :3000 |
| `npm run dev:api` | FastAPI only on :8000 |
| `npm run dev:agents` | All four Fetch.ai uAgents in one Bureau on :8100 — Agentverse-discoverable + real agent-to-agent communication |

Run agents as **separate processes** instead of the Bureau (each binds its own port
8100–8103 and uses `FETCH_CONNECTIVITY`; note cross-process A2A needs Almanac/ledger
network access):

```bash
cd api && . .venv/bin/activate && python3.13 fetch_agents/run_agent.py orchestrator  # :8100
cd api && . .venv/bin/activate && python3.13 fetch_agents/run_agent.py hazard        # :8101
cd api && . .venv/bin/activate && python3.13 fetch_agents/run_agent.py response      # :8102
cd api && . .venv/bin/activate && python3.13 fetch_agents/run_agent.py advisory      # :8103
```

## Agent-to-agent communication

The four agents collaborate via Fetch.ai uAgents messaging. The orchestrator runs a
**pipeline**: it dispatches the incident to the Hazard Intelligence Agent, threads that
assessment into the Response Coordination Agent, threads both into the Public Advisory
Agent, then synthesizes the bridge brief — each specialist builds on the previous one's
output. Messaging uses `ctx.send_and_receive` with an internal `SpecialistQuery` /
`SpecialistReport` model pair (`api/fetch_agents/messages.py`), kept separate from the
public chat protocol used for Agentverse discovery.

`npm run dev:agents` runs the whole fleet in one **Bureau**, which routes messages between
agents in-process and sends one demo incident through the pipeline on startup:

```bash
npm run dev:agents
# custom scenario / options:
cd api && . .venv/bin/activate && python fetch_agents/run_fleet.py "Bilge alarm on Deck 3; smoke in the Engine Room"
cd api && . .venv/bin/activate && python fetch_agents/run_fleet.py --once      # exit after the demo brief
cd api && . .venv/bin/activate && python fetch_agents/run_fleet.py --no-demo   # just run the discoverable fleet
```

You'll see the orchestrator dispatch to each specialist (with `Received A2A query … building
on it`) and print the assembled brief.

> **Why a Bureau?** Cross-process uAgent messaging resolves peer addresses via the Fetch.ai
> Almanac (ledger network acc

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 138 recognized source files, 1055 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code
- Docker (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 159)

```
.env.example
.gitignore
.history/.env_20260620182505
.history/.env_20260620190808
.history/.env_20260620190825
.history/.env_20260620190833
.history/.env_20260620224657
.history/.env_20260620230741
.history/api/.env_20260620165836
.history/api/.env_20260620225001
.history/api/.env_20260620225019
.history/api/.env_20260620225043
.history/api/.env_20260620225103
AI Hackathon 2026 Hacker Guide.txt
api/app/__init__.py
api/app/config.py
api/app/connectors/__init__.py
api/app/connectors/arize_client.py
api/app/connectors/browserbase_client.py
api/app/connectors/calfire.py
api/app/connectors/deepgram_client.py
api/app/connectors/nasa_firms.py
api/app/connectors/nws.py
api/app/connectors/sentry_setup.py
api/app/connectors/usgs_water.py
api/app/main.py
api/app/models.py
api/app/routes/__init__.py
api/app/routes/agents.py
api/app/routes/alerts.py
api/app/routes/briefings.py
api/app/routes/command.py
api/app/routes/fetch.py
api/app/routes/incidents.py
api/app/routes/memory.py
api/app/routes/observability.py
api/app/routes/reports.py
api/app/routes/tasks.py
api/app/routes/voice.py
api/app/routes/websocket.py
api/app/schemas.py
api/app/seed.py
api/app/services/__init__.py
api/app/services/agent_memory.py
api/app/services/agent_orchestrator.py
api/app/services/agentspan_planner.py
api/app/services/alert_drafter.py
api/app/services/command_center.py
api/app/services/compression/__init__.py
api/app/services/compression/adapters.py
api/app/services/compression/agentic.py
api/app/services/compression/codebook.py
api/app/services/compression/compressor.py
api/app/services/compression/dedup.py
api/app/services/compression/dictionary.py
api/app/services/compression/optimize.py
api/app/services/compression/retrieval.py
api/app/services/compression/salience.py
api/app/services/compression/schema.py
api/app/services/compression/tokens.py
api/app/services/fetch_client.py
api/app/services/fetch_specialists.py
api/app/services/gemini_client.py
api/app/services/geocoding.py
api/app/services/incident_store.py
api/app/services/iris_memory.py
api/app/services/redis_memory.py
api/app/services/redis_store.py
api/app/services/redis_vector.py
api/app/services/simulation.py
api/app/services/task_planner.py
api/app/services/verification.py
api/crisis_room.proto
api/fetch_agents/__init__.py
api/fetch_agents/bootstrap.py
api/fetch_agents/messages.py
api/fetch_agents/orchestrator.py
api/fetch_agents/register.py
api/fetch_agents/registry.py
api/fetch_agents/run_agent.py
api/fetch_agents/run_fleet.py
api/fetch_agents/specialist.py
api/pyproject.toml
api/scripts/agentic_compress.py
api/scripts/arize_eval.py
api/scripts/compression_benchmark.py
api/scripts/compression_eval.py
api/scripts/compression_retrieval.py
api/scripts/compression_verify.py
api/scripts/compression_vs_llmlingua.py
api/scripts/crisp_report.py
api/scripts/seed_redis.py
api/scripts/token_compression_demo.py
api/tests/test_compression.py
apps/web/app/api/observability/demo/route.ts
apps/web/app/briefing/page.tsx
apps/web/app/dashboard/page.tsx
apps/web/app/error.tsx
apps/web/app/global-error.tsx
apps/web/app/incident/[id]/page.tsx
apps/web/app/layout.tsx
apps/web/app/page.tsx
apps/web/app/tasks/page.tsx
apps/web/app/voice-report/page.tsx
apps/web/components/AgentRoom.tsx
apps/web/components/CommandConsole.tsx
apps/web/components/CommandDashboard.tsx
apps/web/components/EvidencePanel.tsx
apps/web/components/HazardMap.tsx
apps/web/components/IncidentTimeline.tsx
apps/web/components/PublicAlertDraft.tsx
apps/web/components/RedisMemoryPanel.tsx
apps/web/components/SeverityBadge.tsx
apps/web/components/SourceBadge.tsx
apps/web/components/TaskBoard.tsx
apps/web/components/TopBar.tsx
apps/web/components/VoiceReportPanel.tsx
apps/web/instrumentation.ts
apps/web/lib/api.ts
apps/web/lib/mockDrill.ts
[39 more files omitted for size]
```

### Dependencies

- api/pyproject.toml: agentspan, arize@>=7.19.0, arize-otel@>=0.7.0, fastapi@>=0.111.0, httpx@>=0.27.0, httpx@>=0.27.0, llmlingua@>=0.2.2, numpy@>=1.26.0, openai@>=1.40.0, openinference-instrumentation-openai@>=0.1.18, pandas@>=2.2.0, pydantic@>=2.7.0, pydantic-settings@>=2.2.1, pytest@>=8.2.0, python-dotenv@>=1.0.1, python-multipart@>=0.0.9, redis@>=6.4.0, redis-agent-memory@>=0.0.4, redisvl@>=0.3.0, sentence-transformers@>=3.0.0, sentry-sdk[fastapi]@>=2.19.0, tiktoken@>=0.7.0, tiktoken@>=0.7.0, uagents@>=0.23.0, uvicorn[standard]@>=0.30.0
- apps/web/package.json: @sentry/nextjs@^10.59.0, @types/node@^20.14.0, @types/react@^18.3.0, @types/react-dom@^18.3.0, autoprefixer@^10.4.19, eslint@^8.57.0, eslint-config-next@^14.2.3, next@^14.2.3, postcss@^8.4.38, react@^18.3.1, react-dom@^18.3.1, tailwindcss@^3.4.3, typescript@^5.4.5
- package.json: @playwright/test@^1.61.0

### Recent commits (newest first)

- docs: modify redis document
- fix(redis): stop latching Redis 'disabled' on transient errors
- Add FloodFire CrisisRoom pitch deck with Navy damage-control narrative
- fix: fix redis, and task status pannel
- feat(agentspan): durable risk/action planning via AgentSpan, powered by ASI:One
- docs: replace technical markdown with visual technical deck
- docs: consolidate to deck + single technical deep-dive; split agentic deck slide
- Add bidirectional Deepgram voice dispatcher to command console
- Add agentic self-improving compressor (cutting-edge crux)
- Rename vessel MV Resolute to RSS Endeavour
- docs: add query-aware retrieval as 6th trick in ELI5 deck
- Add vessel schematic and position incident markers by compartment
- fix: add redis storage, redis iris
- Redesign agents to cohesive 4-agent set; remove Crew Advisory Draft
- CRISP: add query-aware retrieval (MMR) stage + per-role benchmark
- docs: simplify deck title slide
- docs: add ELI5 slide deck (self-contained HTML) for CRISP
- docs: add ELI5 explainer for CRISP compression
- CRISP: self-tuning Pareto optimizer, amortized dictionary, self-verifying round-trip + visual report
- CRISP: add semantic dedup stage, LLMLingua-2 adapter, and benchmarks

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

### IDL_USAGE.md

```markdown
# IDL (Interface Definition Language) Usage Guide

This project includes multiple IDL approaches for frontend-backend type safety:

## 1. Protocol Buffers (protobuf) - `crisis_room.proto`

**Location:** `/api/crisis_room.proto`

### Setup for Python (Backend)

```bash
# Install protobuf compiler and Python library
pip install grpcio-tools protobuf

# Generate Python code from .proto file
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. crisis_room.proto
```

This generates:
- `crisis_room_pb2.py` - Message classes
- `crisis_room_pb2_grpc.py` - Service stubs

### Setup for TypeScript (Frontend)

```bash
# Install dependencies
npm install --save-dev @protobuf-ts/plugin @protobuf-ts/runtime @protobuf-ts/runtime-rpc

# Generate TypeScript code
npx protoc --ts_out apps/web/lib --proto_path api api/crisis_room.proto
```

### When to use Protobuf:
- ✅ Need binary serialization for performance
- ✅ Using gRPC for communication
- ✅ Strict schema validation
- ❌ Overhead for simple REST APIs

---

## 2. TypeScript Shared Types - `shared-types.ts`

**Location:** `/shared-types.ts`

### Setup

**For Frontend (Next.js):**
```bash
# Copy to your web app
cp shared-types.ts apps/web/lib/types.ts
```

**Usage in Frontend:**
```typescript
import { Incident, IncidentState, IncidentType } from '@/lib/types';

// Type-safe API calls
const incident: Incident = await fetch('/api/incidents/123').then(r => r.json());
```

**For Backend (Python):**
You're already using Pydantic models in `api/app/models.py`. The TypeScript types mirror these exactly.

### When to use Shared Types:
- ✅ Using REST/JSON APIs (like FastAPI)
- ✅ Quick setup, no code generation
- ✅ Direct TypeScript integration
- ⚠️ Manual sync required between Python and TypeScript

---

## 3. OpenAPI/Swagger (Auto-generated from FastAPI)

**Your FastAPI app already generates OpenAPI specs!**

### Access the Schema:
```bash
# Start your API server
cd api
uvicorn app.main:app --reload

# View interactive docs
open http://localhost:8000/docs

# Download OpenAPI JSON schema
curl http://localhost:8000/openapi.json > openapi.json
```

### Generate TypeScript from OpenAPI:
```bash
# Install openapi-typescript
npm install --save-dev openapi-typescript

# Generate types
npx openapi-typescript http://localhost:8000/openapi.json -o apps/web/lib/api-types.ts
```

### When to use OpenAPI:
- ✅ Already using FastAPI
- ✅ Auto-generated from your Python code
- ✅ No manual maintenance
- ✅ Industry standard

---

## Recommended Approach for Your Project

Given that you're using **FastAPI + Next.js**, I recommend:

### **Option A: OpenAPI (Easiest)**
1. Use FastAPI's built-in OpenAPI generation
2. Generate TypeScript types with `openapi-typescript`
3. Zero maintenance - types auto-update from your Pydantic models

### **Option B: Shared TypeScript Types (Simplest)**
1. Copy `shared-types.ts` to `apps/web/lib/types.ts`
2. Import and use in your React components
3. Manually sync when you change Python models

### *
[truncated — 1566 more characters]
```

### REDIS_SETUP.md

```markdown
# Redis Stack Setup for FloodFire CrisisRoom

## Quick Start with Redis Stack

### Option 1: Docker (Recommended)
```bash
# Run Redis Stack with all modules (RediSearch, RedisJSON, etc.)
docker run -d \
  --name redis-stack \
  -p 6379:6379 \
  -p 8001:8001 \
  redis/redis-stack:latest
```

**Redis Insight** will be available at: http://localhost:8001

### Option 2: Homebrew (Mac)
```bash
brew tap redis-stack/redis-stack
brew install redis-stack
redis-stack-server
```

### Option 3: Redis Cloud (Production)
1. Sign up at https://redis.com/try-free/
2. Create a database with RediSearch module
3. Copy connection string to `.env`:
```bash
REDIS_URL=redis://default:password@your-redis-cloud.com:12345
```

---

## Verify Installation

### Check Redis is Running
```bash
redis-cli ping
# Should return: PONG
```

### Check RediSearch Module
```bash
redis-cli MODULE LIST
# Should show: search, json, timeseries, bloom, etc.
```

### Test Vector Search
```bash
# Create a test index
redis-cli FT.CREATE test:idx ON JSON PREFIX 1 test: SCHEMA $.name TEXT

# Add test data
redis-cli JSON.SET test:1 $ '{"name":"hello world"}'

# Search
redis-cli FT.SEARCH test:idx "hello"
```

---

## Environment Configuration

Create `/api/.env`:
```bash
# Redis Configuration
REDIS_URL=redis://localhost:6379

# Optional: Redis Cloud
# REDIS_URL=redis://default:password@your-redis-cloud.com:12345
# REDIS_ACCOUNT_KEY=your_account_key
# REDIS_API_KEY=your_api_key

# AI API Keys (for embeddings)
ANTHROPIC_API_KEY=your_key_here
GEMINI_API_KEY=your_key_here
```

---

## Verify FloodFire Integration

### 1. Start the API
```bash
cd api
uvicorn app.main:app --reload
```

### 2. Check Redis Capabilities
```bash
curl http://localhost:8000/api/incidents/inc-test/memory/capabilities | jq
```

Expected output:
```json
{
  "redis_connected": true,
  "streams_enabled": true,
  "lexical_search_enabled": true,
  "redisearch_available": true,
  "vector_search_ready": true,
  "index_name": "floodfire:memory:index",
  "memory_count": 0
}
```

### 3. Create Test Data
```bash
# Create an incident
curl -X POST http://localhost:8000/api/incidents \
  -H "Content-Type: application/json" \
  -d '{"name": "Test Incident"}'

# Send a command (creates memory)
curl -X POST http://localhost:8000/api/incidents/inc-xxx/command \
  -H "Content-Type: application/json" \
  -d '{"text": "Fire detected in engine room"}'
```

### 4. Verify Data in Redis
```bash
# List all keys
redis-cli KEYS "floodfire:*"

# Check memory record
redis-cli HGETALL floodfire:memory:mem-xxx

# Check vector index
redis-cli FT.INFO floodfire:vector:index

# Search memories
redis-cli FT.SEARCH floodfire:vector:index "@content:fire" RETURN 3 title content
```

---

## Redis Insight Setup

### Connect to Local Redis
1. Open http://localhost:8001
2. Click "Add Redis Database"
3. Enter:
   - **Host**: localhost
   - **Port**: 6379
   - **Name**: FloodFire CrisisRoom

### Explore Data
1. Click "Browser" tab
2. Search for: `floodfire:*`
3. Inspec
[truncated — 2241 more characters]
```

### docker-compose.yml

```yaml
services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  api:
    image: python:3.11-slim
    working_dir: /app
    volumes:
      - ./api:/app
    env_file:
      - .env.example
    ports:
      - "8000:8000"
    command: sh -c "pip install -e . && uvicorn app.main:app --host 0.0.0.0 --port 8000"
    depends_on:
      - redis

  web:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - .:/app
    env_file:
      - .env.example
    ports:
      - "3000:3000"
    command: sh -c "npm install && npm run dev:web -- --hostname 0.0.0.0"
    depends_on:
      - api

  # postgres:
  #   image: postgres:16-alpine
  #   environment:
  #     POSTGRES_PASSWORD: floodfire
  #   ports:
  #     - "5432:5432"

```

### package.json

```
{
  "name": "crisis-room",
  "private": true,
  "workspaces": [
    "apps/web"
  ],
  "scripts": {
    "dev": "npm install && sh -c 'set -a; [ -f .env ] && . ./.env; set +a; (cd api && python3 -m venv .venv && . .venv/bin/activate && pip install -e . && uvicorn app.main:app --reload) & API_PID=$!; npm run dev:web & WEB_PID=$!; trap \"kill $API_PID $WEB_PID\" EXIT INT TERM; wait $API_PID $WEB_PID'",
    "dev:web": "npm run dev --workspace apps/web",
    "dev:api": "sh -c 'set -a; [ -f .env ] && . ./.env; set +a; cd api && . .venv/bin/activate && uvicorn app.main:app --reload'",
    "dev:agents": "sh -c 'set -a; [ -f .env ] && . ./.env; set +a; cd api && python3 -m venv .venv && . .venv/bin/activate && pip install -e . -q && python fetch_agents/run_fleet.py'",
    "dev:agentspan": "sh -c 'set -a; [ -f .env ] && . ./.env; set +a; export OPENAI_API_KEY=\"${OPENAI_API_KEY:-$ASI_ONE_API_KEY}\"; cd api && python3 -m venv .venv && . .venv/bin/activate && pip install -e .[agentspan] -q && agentspan server start'",
    "build:web": "npm run build --workspace apps/web",
    "lint:web": "npm run lint --workspace apps/web",
    "test:e2e": "playwright test",
    "test:e2e:ui": "playwright test --ui",
    "test:e2e:report": "playwright show-report",
    "test:e2e:install": "playwright install --with-deps chromium"
  },
  "devDependencies": {
    "@playwright/test": "^1.61.0"
  }
}

```

### api/pyproject.toml

```
[project]
name = "floodfire-crisisroom-api"
version = "0.1.0"
description = "FastAPI scaffold for FloodFire CrisisRoom"
requires-python = ">=3.11"
dependencies = [
  "fastapi>=0.111.0",
  "uvicorn[standard]>=0.30.0",
  "pydantic>=2.7.0",
  "pydantic-settings>=2.2.1",
  "python-dotenv>=1.0.1",
  "openai>=1.40.0",
  "uagents>=0.23.0",
  "redis>=6.4.0",
  "redisvl>=0.3.0",
  "redis-agent-memory>=0.0.4",
  "sentence-transformers>=3.0.0",
  "numpy>=1.26.0",
  "httpx>=0.27.0",
  "python-multipart>=0.0.9",
  "sentry-sdk[fastapi]>=2.19.0",
  "arize-otel>=0.7.0",
  "openinference-instrumentation-openai>=0.1.18",
  "arize>=7.19.0",
  "pandas>=2.2.0",
]

[project.optional-dependencies]
test = ["pytest>=8.2.0", "httpx>=0.27.0"]
compression = ["tiktoken>=0.7.0"]
compression-ml = ["tiktoken>=0.7.0", "llmlingua>=0.2.2"]
agentspan = ["agentspan"]

[tool.pytest.ini_options]
pythonpath = ["."]
testpaths = ["tests"]

[tool.setuptools.packages.find]
include = ["app*"]

```

### apps/web/package.json

```
{
  "name": "@floodfire/web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@sentry/nextjs": "^10.59.0",
    "@types/node": "^20.14.0",
    "@types/react": "^18.3.0",
    "@types/react-dom": "^18.3.0",
    "next": "^14.2.3",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "typescript": "^5.4.5"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.19",
    "eslint": "^8.57.0",
    "eslint-config-next": "^14.2.3",
    "postcss": "^8.4.38",
    "tailwindcss": "^3.4.3"
  }
}

```

### api/app/main.py

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.connectors.arize_client import init_arize
from app.connectors.sentry_setup import init_sentry
from app.routes import agents, alerts, briefings, command, fetch, incidents, memory, observability, reports, tasks, voice, websocket


@asynccontextmanager
async def lifespan(_: FastAPI):
    init_sentry()
    init_arize()
    yield


app = FastAPI(title="FloodFire Ship Command API", version="0.1.0", lifespan=lifespan)

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


@app.get("/")
def health_check():
    return {"status": "ok", "service": "FloodFire Ship Command API"}


app.include_router(observability.router)
app.include_router(fetch.router)
app.include_router(incidents.router)
app.include_router(command.router)
app.include_router(memory.router)
app.include_router(reports.router)
app.include_router(agents.router)
app.include_router(tasks.router)
app.include_router(briefings.router)
app.include_router(alerts.router)
app.include_router(voice.router)
app.include_router(websocket.router)

```

### apps/web/app/layout.tsx

```typescript
import "../styles/globals.css";
import type { ReactNode } from "react";

export const metadata = {
  title: "FloodFire CrisisRoom",
  description: "Multi-agent shipboard command dashboard for fire, flooding, smoke, and crew coordination"
};

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

```

### apps/web/app/page.tsx

```typescript
import Link from "next/link";

export default function HomePage() {
  return <main className="flex min-h-screen items-center justify-center p-8"><div className="ops-card max-w-2xl rounded-3xl p-8 text-center"><p className="text-xs font-bold uppercase tracking-[0.35em] text-ops-amber">Shipboard AI Command MVP</p><h1 className="mt-4 text-5xl font-black text-white">FloodFire CrisisRoom</h1><p className="mt-4 text-slate-300">A multi-agent shipboard command dashboard for fire, flooding, smoke, crew coordination, and passenger advisory drafting.</p><Link href="/dashboard" className="mt-8 inline-block rounded-xl bg-ops-amber px-5 py-3 font-black text-ops-ink">Open Command Dashboard</Link></div></main>;
}

```

### apps/web/app/tasks/page.tsx

```typescript
import { TaskBoard } from "@/components/TaskBoard";
import { mockIncidentState } from "@/lib/mockIncident";

export default function TasksPage() {
  return <main className="min-h-screen p-6"><TaskBoard tasks={mockIncidentState.tasks} /></main>;
}

```

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