import { type Express } from "express"; import { z } from "zod"; import { ExternalInformationClient, geocodeInput, webSearchInput } from "../product/external-information-client.js"; import { isTrustedInternalBridgeRequest } from "../product/internal-bridge-auth.js"; import { getRuntimeSessionContext } from "../runtime/sessionContext.js"; export function registerExternalInformationBridge(app: Express, options: { internalToken: string; client?: ExternalInformationClient; }) { const client = options.client ?? new ExternalInformationClient(); for (const name of ["web-search", "geocode"] as const) { app.post(`/internal/tools/${name}`, async (req, res) => { if (!isTrustedInternalBridgeRequest(req, options.internalToken)) { res.status(403).json({ message: "forbidden" }); return; } const parsed = z.object({ session_id: z.string().min(1).max(128), input: name === "web-search" ? webSearchInput : geocodeInput }).strict().safeParse(req.body); if (!parsed.success) { res.status(400).json({ message: "invalid external information input" }); return; } const context = getRuntimeSessionContext(parsed.data.session_id); if (!context || context.learningMode === "review") { res.status(403).json({ message: "active session context required" }); return; } const controller = new AbortController(); const cancel = () => { if (!res.writableEnded) controller.abort(); }; res.once("close", cancel); try { const result = name === "web-search" ? await client.webSearch(context.sessionId, webSearchInput.parse(parsed.data.input), controller.signal) : await client.geocode(context.sessionId, geocodeInput.parse(parsed.data.input), controller.signal); if (!controller.signal.aborted) res.json(result); } catch (error) { const status = error && typeof error === "object" && "status" in error && typeof error.status === "number" ? error.status : 502; if (!controller.signal.aborted) res.status(status).json({ message: error instanceof Error ? error.message : "external information unavailable" }); } finally { res.removeListener("close", cancel); } }); } }