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.1 KB · 53 lines typescript
Raw Blame History
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