revert: remove unused Agent PostgreSQL persistence
This commit is contained in:
+71
-51
@@ -2,17 +2,20 @@ import { randomUUID } from "node:crypto";
|
||||
|
||||
import { logger } from "../logger.js";
|
||||
import { type OpencodeRuntimeAdapter } from "../runtime/opencode.js";
|
||||
import { RuntimeSessionStore } from "../session/runtimeSessionStore.js";
|
||||
import {
|
||||
buildToolSessionScopeKey,
|
||||
ToolSessionContextStore,
|
||||
} from "../session/toolContextStore.js";
|
||||
import { toActorKey, toProjectKey } from "../utils/fileStore.js";
|
||||
|
||||
export type SessionBinding = {
|
||||
clientSessionId: string;
|
||||
sessionId: string;
|
||||
runtimeSessionId: string;
|
||||
startedAt: number;
|
||||
};
|
||||
|
||||
export type SessionContext = {
|
||||
sessionId: string;
|
||||
clientSessionId: string;
|
||||
accessToken?: string;
|
||||
projectId?: string;
|
||||
userId?: string;
|
||||
@@ -25,15 +28,15 @@ export type ChatRequestContext = SessionContext & {
|
||||
};
|
||||
|
||||
export class ChatSessionBridge {
|
||||
// runtime session 仅在单次请求生命周期内有效;线程连续性由 sessionId 对应的持久状态承担。
|
||||
// runtime session 仅在单次请求生命周期内有效;线程连续性由 clientSessionId 对应的持久状态承担。
|
||||
private readonly activeRuntimeSessions = new Map<string, string>();
|
||||
private readonly activeSensitiveContexts = new Map<string, ChatRequestContext>();
|
||||
private readonly runtimeSessionStore = new RuntimeSessionStore();
|
||||
private readonly toolContextStore = new ToolSessionContextStore();
|
||||
|
||||
constructor(private readonly runtime: OpencodeRuntimeAdapter) {}
|
||||
|
||||
async resolve(context: {
|
||||
sessionId?: string;
|
||||
clientSessionId?: string;
|
||||
accessToken?: string;
|
||||
projectId?: string;
|
||||
traceId?: string;
|
||||
@@ -44,24 +47,30 @@ export class ChatSessionBridge {
|
||||
created: boolean;
|
||||
}> {
|
||||
const requestContext = this.buildRequestContext(context);
|
||||
await this.abortActiveRuntime(requestContext.sessionId);
|
||||
await this.abortActiveRuntime(requestContext.clientSessionId);
|
||||
|
||||
const session = await this.runtime.createSession(requestContext.sessionId);
|
||||
const session = await this.runtime.createSession(requestContext.clientSessionId);
|
||||
const binding: SessionBinding = {
|
||||
sessionId: requestContext.sessionId,
|
||||
runtimeSessionId: session.id,
|
||||
clientSessionId: requestContext.clientSessionId,
|
||||
sessionId: session.id,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
this.activeRuntimeSessions.set(requestContext.sessionId, session.id);
|
||||
this.activeSensitiveContexts.set(session.id, requestContext);
|
||||
await this.runtimeSessionStore.write({
|
||||
runtimeSessionId: session.id,
|
||||
const sessionScopeKey = buildToolSessionScopeKey(
|
||||
requestContext.actorKey,
|
||||
requestContext.projectKey,
|
||||
requestContext.clientSessionId,
|
||||
);
|
||||
this.activeRuntimeSessions.set(requestContext.clientSessionId, session.id);
|
||||
this.activeSensitiveContexts.set(sessionScopeKey, requestContext);
|
||||
await this.toolContextStore.write({
|
||||
actorKey: requestContext.actorKey,
|
||||
allowLearningWrite: true,
|
||||
sessionId: requestContext.sessionId,
|
||||
clientSessionId: requestContext.clientSessionId,
|
||||
learningMode: "interactive",
|
||||
projectId: requestContext.projectId,
|
||||
projectKey: requestContext.projectKey,
|
||||
sessionId: session.id,
|
||||
sessionScopeKey,
|
||||
traceId: requestContext.traceId,
|
||||
});
|
||||
|
||||
@@ -72,59 +81,58 @@ export class ChatSessionBridge {
|
||||
return this.activeRuntimeSessions.size;
|
||||
}
|
||||
|
||||
createSessionId() {
|
||||
createClientSessionId() {
|
||||
return `agent-${randomUUID().slice(0, 12)}`;
|
||||
}
|
||||
|
||||
getActiveSensitiveContext(runtimeSessionId: string) {
|
||||
return this.activeSensitiveContexts.get(runtimeSessionId) ?? null;
|
||||
getActiveSensitiveContext(sessionScopeKey: string) {
|
||||
return this.activeSensitiveContexts.get(sessionScopeKey) ?? null;
|
||||
}
|
||||
|
||||
async abort(context: { sessionId?: string }): Promise<SessionBinding | null> {
|
||||
const sessionId = context.sessionId?.trim();
|
||||
async abort(context: {
|
||||
clientSessionId?: string;
|
||||
}): Promise<SessionBinding | null> {
|
||||
const clientSessionId = context.clientSessionId?.trim();
|
||||
if (!clientSessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sessionId = this.activeRuntimeSessions.get(clientSessionId);
|
||||
if (!sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const runtimeSessionId = this.activeRuntimeSessions.get(sessionId);
|
||||
if (!runtimeSessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.abortActiveRuntime(sessionId);
|
||||
await this.abortActiveRuntime(clientSessionId);
|
||||
return {
|
||||
clientSessionId,
|
||||
sessionId,
|
||||
runtimeSessionId,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
async releaseRuntimeSession(sessionId: string, runtimeSessionId: string) {
|
||||
const activeSessionId = this.activeRuntimeSessions.get(sessionId);
|
||||
if (activeSessionId === runtimeSessionId) {
|
||||
this.activeRuntimeSessions.delete(sessionId);
|
||||
async releaseRuntimeSession(clientSessionId: string, sessionId: string) {
|
||||
const activeSessionId = this.activeRuntimeSessions.get(clientSessionId);
|
||||
if (activeSessionId === sessionId) {
|
||||
this.activeRuntimeSessions.delete(clientSessionId);
|
||||
}
|
||||
this.activeSensitiveContexts.delete(runtimeSessionId);
|
||||
await this.runtimeSessionStore.release(runtimeSessionId).catch((error) => {
|
||||
logger.debug(
|
||||
{ runtimeSessionId, err: error },
|
||||
"failed to cleanup persisted runtime session",
|
||||
);
|
||||
this.activeSensitiveContexts.delete(findScopeKey(this.activeSensitiveContexts, clientSessionId));
|
||||
await this.toolContextStore.remove(sessionId).catch((error) => {
|
||||
logger.debug({ sessionId, err: error }, "failed to cleanup runtime tool context");
|
||||
});
|
||||
await this.runtime.abortSession(runtimeSessionId).catch((error) => {
|
||||
logger.debug({ runtimeSessionId, err: error }, "failed to cleanup runtime session");
|
||||
await this.runtime.abortSession(sessionId).catch((error) => {
|
||||
logger.debug({ sessionId, err: error }, "failed to cleanup runtime session");
|
||||
});
|
||||
}
|
||||
|
||||
private buildRequestContext(context: {
|
||||
sessionId?: string;
|
||||
clientSessionId?: string;
|
||||
accessToken?: string;
|
||||
projectId?: string;
|
||||
traceId?: string;
|
||||
userId?: string;
|
||||
}): ChatRequestContext {
|
||||
return {
|
||||
sessionId: context.sessionId?.trim() || this.createSessionId(),
|
||||
clientSessionId: context.clientSessionId?.trim() || this.createClientSessionId(),
|
||||
accessToken: context.accessToken,
|
||||
actorKey: toActorKey(context.userId),
|
||||
projectId: context.projectId,
|
||||
@@ -134,25 +142,37 @@ export class ChatSessionBridge {
|
||||
};
|
||||
}
|
||||
|
||||
private async abortActiveRuntime(sessionId: string) {
|
||||
const activeRuntimeSessionId = this.activeRuntimeSessions.get(sessionId);
|
||||
if (!activeRuntimeSessionId) {
|
||||
private async abortActiveRuntime(clientSessionId: string) {
|
||||
const activeSessionId = this.activeRuntimeSessions.get(clientSessionId);
|
||||
if (!activeSessionId) {
|
||||
return;
|
||||
}
|
||||
this.activeRuntimeSessions.delete(sessionId);
|
||||
this.activeSensitiveContexts.delete(activeRuntimeSessionId);
|
||||
await this.runtimeSessionStore.release(activeRuntimeSessionId).catch(() => undefined);
|
||||
await this.runtime.abortSession(activeRuntimeSessionId).catch((error) => {
|
||||
this.activeRuntimeSessions.delete(clientSessionId);
|
||||
this.activeSensitiveContexts.delete(findScopeKey(this.activeSensitiveContexts, clientSessionId));
|
||||
await this.toolContextStore.remove(activeSessionId).catch(() => undefined);
|
||||
await this.runtime.abortSession(activeSessionId).catch((error) => {
|
||||
logger.warn(
|
||||
{ sessionId, runtimeSessionId: activeRuntimeSessionId, err: error },
|
||||
{ clientSessionId, sessionId: activeSessionId, err: error },
|
||||
"failed to abort previous active runtime session",
|
||||
);
|
||||
});
|
||||
await this.runtime.waitForSessionIdle(activeRuntimeSessionId).catch((error) => {
|
||||
await this.runtime.waitForSessionIdle(activeSessionId).catch((error) => {
|
||||
logger.warn(
|
||||
{ sessionId, runtimeSessionId: activeRuntimeSessionId, err: error },
|
||||
{ clientSessionId, sessionId: activeSessionId, err: error },
|
||||
"failed while waiting for previous runtime session to become idle",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const findScopeKey = (
|
||||
contexts: Map<string, ChatRequestContext>,
|
||||
clientSessionId: string,
|
||||
) => {
|
||||
for (const [scopeKey, context] of contexts.entries()) {
|
||||
if (context.clientSessionId === clientSessionId) {
|
||||
return scopeKey;
|
||||
}
|
||||
}
|
||||
return clientSessionId;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user