spb/search-box Public
Agentic web research engine — hypotheses, verbatim evidence, contradictions, sourced answers streamed live. Claude Opus 5 + Firecrawl + PostgreSQL.
TypeScript 76.9%
CSS 18.7%
SQL 2.1%
JavaScript 1.8%
Shell 0.5%
1<!--2Search-box.ai3Author: Simon-Pierre Boucher4Contact: contact@spboucher.ai5File: docs/agent-loop.md6Description: How the orchestrator loop works.7-->89# Agent loop1011`packages/agent/src/orchestrator.ts` runs a manual Claude tool-use loop:12131. One streaming Messages API call per turn (`effort: medium`, prompt-cached system).142. All `tool_use` blocks in the response execute sequentially; all results return in one15 user message (assistant content — thinking blocks included — is echoed back verbatim).163. The loop ends when the model calls `finish_research`, stops calling tools, or budgets17 run out (one warning turn, then hard stop).1819## Tools2021| Tool | Effect |22|---|---|23| `set_objectives` | research plan (emits `plan.updated`) |24| `web_search` | Firecrawl search; results become `found` sources |25| `fetch_url` | Firecrawl scrape → stored (≤120k chars), returned to model (≤14k) wrapped in `<untrusted_web_content>` |26| `read_source` | re-read stored content with offset paging — costs no budget |27| `add_claim` / `update_claim` | hypothesis lifecycle with confidence 0..1 |28| `add_evidence` | verbatim quote tied to claim + stance; soft-verified against stored content |29| `add_contradiction` | first-class disagreement record |30| `report_progress` | public narration (`thought` event) |31| `finish_research` | closes the loop, hands off to synthesis |3233## Safety3435- Budgets (`maxSearches`, `maxScrapes`, `maxToolCalls`, `maxModelTurns`, `deadlineMs`) are36 hard bounds enforced by the app, not suggestions to the model.37- Tool inputs are zod-validated; invalid input returns an `is_error` tool result and the38 session continues (a single tool failure never kills a session).39- Scraped content is wrapped in `<untrusted_web_content>` and the system prompt instructs40 the model to treat it as evidence, never as instructions (prompt-injection defense).41- URLs pass an SSRF guard (`packages/firecrawl/src/url-guard.ts`) blocking private ranges,42 localhost and metadata endpoints.43- `stop_reason: "refusal"` fails the session gracefully with a user-visible error.4445## Synthesis4647`packages/agent/src/synthesis.ts` assigns mechanical citation indices, builds the evidence48base (claims → stances → verbatim quotes labeled `[n]`), and streams the answer token-by-token49(`answer.delta` events, buffered ~160 chars / 400 ms per event row).50