SPB Git

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%
2.3 KB · 50 lines markdown
Rendered Raw Blame History
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