重构会话管理功能,由后端 opencode 发放 sessionId,后端做 scope

This commit is contained in:
2026-05-21 15:41:46 +08:00
parent 7e63d38cf5
commit 5d80961930
20 changed files with 816 additions and 390 deletions
+37 -20
View File
@@ -5,6 +5,7 @@ import express from "express";
import { SessionHistoryStore } from "./history/store.js";
import { ChatSessionBridge } from "./chat/sessionBridge.js";
import { config } from "./config.js";
import { ConversationStore } from "./conversations/store.js";
import { logger } from "./logger.js";
import { LearningOrchestrator } from "./learning/orchestrator.js";
import { MemoryStore } from "./memory/store.js";
@@ -12,13 +13,12 @@ import { ResultReferenceResolver } from "./results/resolver.js";
import { ResultReferenceStore } from "./results/store.js";
import { buildChatRouter } from "./routes/chat.js";
import { opencodeRuntime } from "./runtime/opencode.js";
import { SessionRegistry } from "./session/registry.js";
import { ToolSessionContextStore } from "./session/toolContextStore.js";
import { DynamicHttpExecutor } from "./tools/dynamicHttpExecutor.js";
const app = express();
const registry = new SessionRegistry(config.SESSION_TTL_SECONDS);
const sessionBridge = new ChatSessionBridge(registry, opencodeRuntime);
const sessionBridge = new ChatSessionBridge(opencodeRuntime);
const conversationStore = new ConversationStore();
const memoryStore = new MemoryStore();
const sessionHistoryStore = new SessionHistoryStore();
const toolContextStore = new ToolSessionContextStore();
@@ -63,12 +63,22 @@ app.post("/internal/tools/dynamic-http-call", async (req, res) => {
return;
}
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : "";
const context = sessionBridge.getSessionContext(sessionId);
const sessionScopeKey =
typeof req.body?.sessionScopeKey === "string" ? req.body.sessionScopeKey : "";
const threadContext = await toolContextStore.read(sessionScopeKey);
const runtimeContext = sessionBridge.getActiveSensitiveContext(sessionScopeKey);
if (!threadContext && !runtimeContext) {
res.status(404).json({
message: "runtime or session context not found",
detail: sessionScopeKey,
});
return;
}
const context = runtimeContext ?? threadContext;
if (!context) {
res.status(404).json({
message: "session context not found",
detail: sessionId,
message: "runtime or session context not found",
detail: sessionScopeKey,
});
return;
}
@@ -83,12 +93,12 @@ app.post("/internal/tools/dynamic-http-call", async (req, res) => {
arguments: req.body?.arguments,
},
{
accessToken: context.accessToken,
accessToken: runtimeContext?.accessToken,
actorKey: context.actorKey,
clientSessionId: context.clientSessionId,
projectId: context.projectId,
projectKey: context.projectKey,
sessionId,
sessionId: context.clientSessionId,
traceId: context.traceId,
},
);
@@ -108,13 +118,14 @@ app.post("/internal/tools/fetch-result-ref", async (req, res) => {
return;
}
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : "";
const sessionScopeKey =
typeof req.body?.sessionScopeKey === "string" ? req.body.sessionScopeKey : "";
const resultRef = typeof req.body?.result_ref === "string" ? req.body.result_ref : "";
const context = sessionBridge.getSessionContext(sessionId);
const context = await toolContextStore.read(sessionScopeKey);
if (!context) {
res.status(404).json({
message: "session context not found",
detail: sessionId,
detail: sessionScopeKey,
});
return;
}
@@ -127,6 +138,7 @@ app.post("/internal/tools/fetch-result-ref", async (req, res) => {
resultRef,
{
actorKey: context.actorKey,
clientSessionId: context.clientSessionId,
projectId: context.projectId,
},
{
@@ -149,13 +161,14 @@ app.post("/internal/tools/store-render-ref", async (req, res) => {
return;
}
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : "";
const sessionScopeKey =
typeof req.body?.sessionScopeKey === "string" ? req.body.sessionScopeKey : "";
const filePath = typeof req.body?.file_path === "string" ? req.body.file_path.trim() : "";
const context = sessionBridge.getSessionContext(sessionId);
const context = await toolContextStore.read(sessionScopeKey);
if (!context) {
res.status(404).json({
message: "session context not found",
detail: sessionId,
detail: sessionScopeKey,
});
return;
}
@@ -170,7 +183,7 @@ app.post("/internal/tools/store-render-ref", async (req, res) => {
clientSessionId: context.clientSessionId,
projectId: context.projectId,
projectKey: context.projectKey,
sessionId,
sessionId: context.clientSessionId,
source: "migration",
traceId: context.traceId,
});
@@ -198,13 +211,14 @@ app.post("/internal/tools/session-search", async (req, res) => {
return;
}
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : "";
const sessionScopeKey =
typeof req.body?.sessionScopeKey === "string" ? req.body.sessionScopeKey : "";
const query = typeof req.body?.query === "string" ? req.body.query : "";
const context = await toolContextStore.read(sessionId);
const context = await toolContextStore.read(sessionScopeKey);
if (!context) {
res.status(404).json({
message: "tool session context not found",
detail: sessionId,
message: "session context not found",
detail: sessionScopeKey,
});
return;
}
@@ -231,7 +245,9 @@ app.use(
buildChatRouter(
sessionBridge,
opencodeRuntime,
conversationStore,
memoryStore,
sessionHistoryStore,
learningOrchestrator,
resultReferenceResolver,
),
@@ -239,6 +255,7 @@ app.use(
const bootstrap = async () => {
await Promise.all([
conversationStore.initialize(),
learningOrchestrator.initialize(),
memoryStore.initialize(),
resultReferenceStore.initialize(),