refactor: centralize gateway module and address review fixes

Move the gateway runtime files into a dedicated core/gateway module and fix follow-up issues in session deletion, history import batching, message IDs, and legacy thread parsing.

Fixes #253

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Harivansh Rathi 2026-03-07 16:53:36 -08:00
parent 753cb935f1
commit c0bba5c38f
10 changed files with 47 additions and 29 deletions

View file

@ -1,4 +1,4 @@
import type { AgentSession } from "./agent-session.js"; import type { AgentSession } from "../agent-session.js";
export function extractMessageText(message: { content: unknown }): string { export function extractMessageText(message: { content: unknown }): string {
if (!Array.isArray(message.content)) { if (!Array.isArray(message.content)) {

View file

@ -0,0 +1,19 @@
export {
createGatewaySessionManager,
GatewayRuntime,
getActiveGatewayRuntime,
sanitizeSessionKey,
setActiveGatewayRuntime,
} from "./runtime.js";
export type {
ChannelStatus,
GatewayConfig,
GatewayMessageRequest,
GatewayMessageResult,
GatewayRuntimeOptions,
GatewaySessionFactory,
GatewaySessionSnapshot,
HistoryMessage,
HistoryPart,
ModelInfo,
} from "./runtime.js";

View file

@ -1,9 +1,9 @@
import type { AgentSession } from "./agent-session.js"; import type { AgentSession } from "../agent-session.js";
import type { import type {
GatewayMessageRequest, GatewayMessageRequest,
GatewayMessageResult, GatewayMessageResult,
GatewaySessionSnapshot, GatewaySessionSnapshot,
} from "./gateway-runtime-types.js"; } from "./types.js";
export interface GatewayQueuedMessage { export interface GatewayQueuedMessage {
request: GatewayMessageRequest; request: GatewayMessageRequest;

View file

@ -8,18 +8,15 @@ import { rm } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
import { URL } from "node:url"; import { URL } from "node:url";
import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { AgentMessage } from "@mariozechner/pi-agent-core";
import type { AgentSession, AgentSessionEvent } from "./agent-session.js"; import type { AgentSession, AgentSessionEvent } from "../agent-session.js";
import { import { extractMessageText, getLastAssistantText } from "./helpers.js";
extractMessageText,
getLastAssistantText,
} from "./gateway-runtime-helpers.js";
import { import {
type GatewayEvent, type GatewayEvent,
type GatewayQueuedMessage, type GatewayQueuedMessage,
HttpError, HttpError,
type ManagedGatewaySession, type ManagedGatewaySession,
} from "./gateway-runtime-internal-types.js"; } from "./internal-types.js";
import { sanitizeSessionKey } from "./gateway-session-manager.js"; import { sanitizeSessionKey } from "./session-manager.js";
import type { import type {
ChannelStatus, ChannelStatus,
GatewayConfig, GatewayConfig,
@ -31,8 +28,8 @@ import type {
HistoryMessage, HistoryMessage,
HistoryPart, HistoryPart,
ModelInfo, ModelInfo,
} from "./gateway-runtime-types.js"; } from "./types.js";
import type { Settings } from "./settings-manager.js"; import type { Settings } from "../settings-manager.js";
import { import {
createVercelStreamListener, createVercelStreamListener,
errorVercelStream, errorVercelStream,
@ -42,7 +39,7 @@ import {
export { export {
createGatewaySessionManager, createGatewaySessionManager,
sanitizeSessionKey, sanitizeSessionKey,
} from "./gateway-session-manager.js"; } from "./session-manager.js";
export type { export type {
ChannelStatus, ChannelStatus,
GatewayConfig, GatewayConfig,
@ -54,7 +51,7 @@ export type {
HistoryMessage, HistoryMessage,
HistoryPart, HistoryPart,
ModelInfo, ModelInfo,
} from "./gateway-runtime-types.js"; } from "./types.js";
let activeGatewayRuntime: GatewayRuntime | null = null; let activeGatewayRuntime: GatewayRuntime | null = null;
@ -966,7 +963,7 @@ export class GatewayRuntime {
const managed = await this.ensureSession(sessionKey); const managed = await this.ensureSession(sessionKey);
const rawMessages = managed.session.messages; const rawMessages = managed.session.messages;
const messages: HistoryMessage[] = []; const messages: HistoryMessage[] = [];
for (const msg of rawMessages) { for (const [index, msg] of rawMessages.entries()) {
if ( if (
msg.role !== "user" && msg.role !== "user" &&
msg.role !== "assistant" && msg.role !== "assistant" &&
@ -975,7 +972,7 @@ export class GatewayRuntime {
continue; continue;
} }
messages.push({ messages.push({
id: `${msg.timestamp}-${msg.role}`, id: `${msg.timestamp}-${msg.role}-${index}`,
role: msg.role, role: msg.role,
parts: this.messageContentToParts(msg), parts: this.messageContentToParts(msg),
timestamp: msg.timestamp, timestamp: msg.timestamp,
@ -1006,7 +1003,8 @@ export class GatewayRuntime {
if (sessionKey === this.primarySessionKey) { if (sessionKey === this.primarySessionKey) {
throw new HttpError(400, "Cannot delete primary session"); throw new HttpError(400, "Cannot delete primary session");
} }
const managed = await this.ensureSession(sessionKey); const managed = this.sessions.get(sessionKey);
if (managed) {
if (managed.processing) { if (managed.processing) {
await managed.session.abort(); await managed.session.abort();
} }
@ -1014,6 +1012,7 @@ export class GatewayRuntime {
managed.unsubscribe(); managed.unsubscribe();
managed.session.dispose(); managed.session.dispose();
this.sessions.delete(sessionKey); this.sessions.delete(sessionKey);
}
await rm(this.getGatewaySessionDir(sessionKey), { await rm(this.getGatewaySessionDir(sessionKey), {
recursive: true, recursive: true,
force: true, force: true,

View file

@ -1,5 +1,5 @@
import { join } from "node:path"; import { join } from "node:path";
import { SessionManager } from "./session-manager.js"; import { SessionManager } from "../session-manager.js";
export function sanitizeSessionKey(sessionKey: string): string { export function sanitizeSessionKey(sessionKey: string): string {
return sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_"); return sessionKey.replace(/[^a-zA-Z0-9._-]/g, "_");

View file

@ -1,5 +1,5 @@
import type { AgentSession } from "./agent-session.js";
import type { ImageContent } from "@mariozechner/pi-ai"; import type { ImageContent } from "@mariozechner/pi-ai";
import type { AgentSession } from "../agent-session.js";
export interface GatewayConfig { export interface GatewayConfig {
bind: string; bind: string;

View file

@ -1,6 +1,6 @@
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
import type { ServerResponse } from "node:http"; import type { ServerResponse } from "node:http";
import type { AgentSessionEvent } from "./agent-session.js"; import type { AgentSessionEvent } from "../agent-session.js";
/** /**
* Write a single Vercel AI SDK v5+ SSE chunk to the response. * Write a single Vercel AI SDK v5+ SSE chunk to the response.

View file

@ -156,7 +156,7 @@ export {
getActiveGatewayRuntime, getActiveGatewayRuntime,
sanitizeSessionKey, sanitizeSessionKey,
setActiveGatewayRuntime, setActiveGatewayRuntime,
} from "./core/gateway-runtime.js"; } from "./core/gateway/index.js";
export { convertToLlm } from "./core/messages.js"; export { convertToLlm } from "./core/messages.js";
export { ModelRegistry } from "./core/model-registry.js"; export { ModelRegistry } from "./core/model-registry.js";
export type { export type {

View file

@ -22,7 +22,7 @@ import { APP_NAME, getAgentDir, getModelsPath, VERSION } from "./config.js";
import { AuthStorage } from "./core/auth-storage.js"; import { AuthStorage } from "./core/auth-storage.js";
import { exportFromFile } from "./core/export-html/index.js"; import { exportFromFile } from "./core/export-html/index.js";
import type { LoadExtensionsResult } from "./core/extensions/index.js"; import type { LoadExtensionsResult } from "./core/extensions/index.js";
import { createGatewaySessionManager } from "./core/gateway-runtime.js"; import { createGatewaySessionManager } from "./core/gateway/index.js";
import { KeybindingsManager } from "./core/keybindings.js"; import { KeybindingsManager } from "./core/keybindings.js";
import { ModelRegistry } from "./core/model-registry.js"; import { ModelRegistry } from "./core/model-registry.js";
import { import {

View file

@ -12,7 +12,7 @@ import {
GatewayRuntime, GatewayRuntime,
type GatewaySessionFactory, type GatewaySessionFactory,
setActiveGatewayRuntime, setActiveGatewayRuntime,
} from "../core/gateway-runtime.js"; } from "../core/gateway/index.js";
import type { GatewaySettings } from "../core/settings-manager.js"; import type { GatewaySettings } from "../core/settings-manager.js";
/** /**