Files
next-tjwater-agent/src/routes/task-publication-bridge.ts
T
jiang 6279a4b555 refactor: consolidate chat state and task bridges
Centralize session scope, chat projections and typed task bridges.
Keep historical interaction replies authoritative and publish terminal state to subscribers. Validate Server contracts before replacing generated artifacts.
2026-11-09 17:55:00 +08:00

65 lines
2.8 KiB
TypeScript

import { type Express } from "express";
import { z } from "zod";
import { isTrustedInternalBridgeRequest } from "../product/internal-bridge-auth.js";
import { operationalTaskClient, type OperationalTaskClient, taskArtifactBridgeSchema } from "../product/operational-task-client.js";
import { taskPresentationBridgeSchema } from "../product/task-presentation.js";
import { readTaskBridgeContext, replyTaskBridgeError } from "./task-bridge-context.js";
const artifactsInputSchema = taskArtifactBridgeSchema.and(z.object({ session_id: z.string().trim().min(1).max(128) }));
export function registerTaskPublicationBridge(app: Express, options: {
internalToken: string;
client?: Pick<OperationalTaskClient, "publishArtifacts" | "publishPresentation">;
}) {
const client = options.client ?? operationalTaskClient;
for (const kind of ["artifacts", "presentation"] as const) {
app.post(`/internal/operational-task/${kind}`, async (req, res) => {
if (!isTrustedInternalBridgeRequest(req, options.internalToken)) {
res.status(403).json({ message: "forbidden" });
return;
}
const bridge = readTaskBridgeContext(req, options.internalToken);
if (!bridge) {
res.status(403).json({ message: "operational task context unavailable" });
return;
}
const { context, runtimeSessionId: sessionId, taskId } = bridge;
if ((kind === "presentation" || req.body?.progress !== undefined) && context.purpose !== "task_execution") {
res.status(403).json({ message: "task execution context required" });
return;
}
try {
if (kind === "presentation") {
const parsed = taskPresentationBridgeSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: "invalid task presentation", detail: parsed.error.flatten() });
return;
}
res.json(await client.publishPresentation(sessionId, taskId, {
requestId: parsed.data.request_id,
sourceSessionId: context.clientSessionId,
command: parsed.data.command,
}));
} else {
const parsed = artifactsInputSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ message: "invalid task artifacts", detail: parsed.error.flatten() });
return;
}
res.json(await client.publishArtifacts(sessionId, taskId, {
requestId: parsed.data.request_id,
sourceSessionId: context.clientSessionId,
evidence: parsed.data.evidence,
executionPlan: parsed.data.execution_plan,
report: parsed.data.report,
progress: parsed.data.progress,
}));
}
} catch (error) {
replyTaskBridgeError(res, error, `task ${kind} publish failed`);
}
});
}
}