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/**2 * Search-box.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: packages/agent/src/run.ts6 * Description: Session runner — creates/executes a full research session end to end.7 */89import { sessions } from "@search-box/db";10import { ResearchState } from "@search-box/research";11import { BudgetsSchema, DEFAULT_BUDGETS, type Budgets } from "@search-box/shared";12import { runOrchestration } from "./orchestrator.js";13import { runSynthesis } from "./synthesis.js";1415export async function createSession(question: string, budgets?: Partial<Budgets>): Promise<string> {16 const resolved = BudgetsSchema.parse({ ...DEFAULT_BUDGETS, ...budgets });17 const session = await sessions.create(question, resolved as unknown as Record<string, number>);18 const state = new ResearchState(session.id);19 await state.emit({ type: "session.started", question });20 return session.id;21}2223/**24 * Executes a created session. A single tool failure never kills the session25 * (handled inside the loop); only unrecoverable errors mark it failed.26 */27export async function runSession(sessionId: string): Promise<void> {28 const session = await sessions.get(sessionId);29 if (!session) throw new Error(`unknown session: ${sessionId}`);30 const budgets = BudgetsSchema.parse({ ...DEFAULT_BUDGETS, ...session.budgets });31 const state = new ResearchState(sessionId);3233 try {34 await state.setStatus("running");35 const result = await runOrchestration(state, session.question, budgets);3637 await state.setStatus("synthesizing");38 const refreshed = await sessions.get(sessionId);39 const { answer } = await runSynthesis(state, session.question, refreshed?.objectives ?? []);4041 await sessions.complete(sessionId, answer);42 await state.emit({ type: "session.completed", answer });43 await state.emit({ type: "session.status", status: "completed" });44 void result;45 } catch (err) {46 const message = err instanceof Error ? err.message : String(err);47 await sessions.fail(sessionId, message);48 await state.emit({ type: "session.failed", error: message });49 await state.emit({ type: "session.status", status: "failed" });50 throw err;51 }52}53