Files
next-tjwater-agent/src/routes/externalInformationBridge.ts
T
jiang 7383ed6d13 feat(agent): migrate OpenCode 2 runtime and sandbox tools
Upgrade the runtime and native plugin API to OpenCode 2.0.24. Restore
session-scoped learning, credential renewal, typed project queries and
controlled frontend actions from the legacy Agent.

Replace host Shell execution with an approved Landlock/seccomp executor
and persistent per-session workspaces. Add typed Bocha search and Tianditu
geocoding bridges using the owning session credential. Require a working
kernel sandbox before startup and persist workspace files in deployments.

Validation completed before this commit: TypeScript checks, 150 Bun tests,
Docker build and UID 10001 sandbox execution, native Shell approval and
session recovery. Tianditu succeeded; Bocha currently reports exhausted
account quota. Unrelated investigation-report contract work is excluded.
2026-10-08 15:46:37 +08:00

34 lines
2.1 KiB
TypeScript

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); }
});
}
}