# Agent summarization logic

This document explains when the research agent stops gathering data and produces a final report, and how you can override that behavior from the UI.

## Automatic summarization (default)

The agent runs a **tool-use loop** in `agent.py` (`run_agent_stream`). There is **no application-level rule** that counts searches, scrapes, or bytes gathered. The Python code does not implement thresholds such as “after 5 searches, summarize.”

Each loop iteration:

1. Claude receives the conversation (initial task + prior tool results).
2. Claude returns a response with a `stop_reason`.
3. The server branches on that reason:

| `stop_reason` | What happens |
|---------------|--------------|
| `"tool_use"` | Tools run (`search_web`, `scrape_url`). Results are appended to `messages`. The loop continues. |
| `"end_turn"` | Claude returned final text only (no more tools). That text is emitted as the **report** and the stream ends. |
| Anything else | Treated as an error. |

**Summarization is decided by the model**, not by fixed code. Claude chooses to keep calling tools until it believes it has enough evidence to write the structured report defined in the system prompt (startup or contract mode). That judgment is influenced by:

- The system prompt (required report sections).
- Tool output quality (empty results, errors, thin snippets).
- The initial user task (company name or contract filters).

So “enough data” means: **Claude stopped requesting tools and produced a final assistant message** (`stop_reason == "end_turn"`).

## What the code does *not* do

- No maximum number of tool calls (unless the API hits a limit).
- No timer or deadline for research phase.
- No similarity or coverage score over collected pages.
- No separate “summarization” API call in the normal path—the final turn is the report.

## Manual summarization (keyboard shortcut)

Press **`Shift + S`** while a run is in progress to **force summarization**:

1. The browser aborts the active `/research` stream.
2. The client sends accumulated tool output to **`POST /summarize`**.
3. The server calls Claude **once, without tools**, with a user message that includes the research log and instructs Claude to write the final report immediately.

Use this when the agent is still searching/scraping but you already have sufficient material, or to save time and API cost.

If no tool results were collected yet, the shortcut has nothing to summarize and shows an error in the activity panel.

## Related shortcuts

| Shortcut | Action |
|----------|--------|
| `Shift + S` | Force summarization (during an active run) |
| `Ctrl + Shift + D` | Download the current report as Markdown |
| `Ctrl + Shift + O` | Upload and view a previously saved report (`.md` / `.txt`) |
| `Ctrl + Shift + L` | Upload a logo (Contract Finder only; stored in browser `localStorage`) |

## Implementation references

- Loop and `stop_reason` handling: `agent.py` → `run_agent_stream()`
- Forced report: `agent.py` → `run_summarize_stream()`, `app.py` → `POST /summarize`
- UI shortcuts and research log accumulation: `templates/index.html`
