feat(context): verify product session scope

This commit is contained in:
2026-09-22 10:43:36 +08:00
parent 88b41ba04c
commit 9faed59f23
12 changed files with 358 additions and 56 deletions
+32 -1
View File
@@ -13,6 +13,11 @@ import { type SessionMetadataStore } from "../sessions/metadataStore.js";
import { type SessionTranscriptStore } from "../sessions/transcriptStore.js";
import { type SessionUiStateStore } from "../sessions/uiStateStore.js";
import { uiRegistryResponse } from "../uiEnvelope/registry.js";
import {
attachTrustedAgentContext,
getLocalAgentContext,
ProductContextError,
} from "../context/localContext.js";
import { registerChatAuxiliaryRoutes } from "./chatAuxiliaryRoutes.js";
import { registerChatInteractionRoutes } from "./chatInteractionRoutes.js";
import { createChatRunState } from "./chatRunState.js";
@@ -48,6 +53,18 @@ export const buildChatRouter = (options: BuildChatRouterOptions) => {
const chatRouter = Router();
const { activeRuns, lastRunStatuses } = runState;
chatRouter.use((req, res, next) => {
void attachTrustedAgentContext(req)
.then(() => next())
.catch((error) => {
const status = error instanceof ProductContextError ? error.status : 503;
res.status(status).json({
code: "PRODUCT_CONTEXT_UNAVAILABLE",
message: error instanceof Error ? error.message : "product identity verification failed",
});
});
});
chatRouter.get("/models", (_req, res) => {
res.json({
default_model: resolveDefaultModel(config.OPENCODE_MODEL),
@@ -63,13 +80,27 @@ export const buildChatRouter = (options: BuildChatRouterOptions) => {
res.json(frontendActionRegistryResponse);
});
chatRouter.post("/frontend-actions/:action_id/result", (req, res) => {
chatRouter.post("/frontend-actions/:action_id/result", async (req, res) => {
const sessionId = req.header("x-agent-session-id")?.trim() ?? "";
if (!sessionId || req.params.action_id !== req.body?.actionId) {
res.status(400).json({ message: "invalid frontend action result" });
return;
}
try {
const context = getLocalAgentContext(req);
const sessionRecord = await sessionMetadataStore.get(
{
actorKey: context.actorKey,
projectId: context.projectId,
projectKey: context.projectKey,
userId: context.userId,
},
sessionId,
);
if (!sessionRecord) {
res.status(404).json({ message: "session not found" });
return;
}
const outcome = frontendActionCoordinator.submitResult(sessionId, req.body);
res.json({
accepted: true,