/** * Gateway * * Standalone daemon for multi-client, multi-app access. * Transport-agnostic: supports both WebSocket and HTTP/SSE. * * Can run standalone or embedded in an external framework. */ import { EventEmitter } from "events"; import type { IncomingMessage as NodeRequest, ServerResponse as NodeResponse } from "http"; import type { Session } from "@agentick/core"; import type { ClientTransport, SendInput } from "@agentick/shared"; import type { GatewayConfig, GatewayEvents, GatewayPlugin } from "./types.js"; import type { StatusPayload } from "./transport-protocol.js"; export declare class Gateway extends EventEmitter { private config; private registry; private sessions; private transports; private startTime; private isRunning; private embedded; /** Pre-compiled map of method paths to procedures */ private methodProcedures; /** Stored schemas for custom methods (method path → raw schema) */ private methodSchemas; /** Stored response schemas for custom methods (method path → raw schema) */ private methodResponseSchemas; /** Stored metadata for custom methods (method path → { description, roles }) */ private methodMeta; /** SSE transport for embedded mode (initialized in constructor when embedded: true) */ private sseTransport; /** Track channel subscriptions: "sessionId:channelName" -> Set of clientIds */ private channelSubscriptions; /** Track unsubscribe functions for core session channels */ private coreChannelUnsubscribes; /** Track client connection times for duration calculation */ private clientConnectedAt; /** Shared local transport instance (created lazily) */ private _localTransport; /** Per-client event buffers for backpressure */ private clientBuffers; /** Sequence counter for DevTools events */ private devToolsSequence; /** Registered plugins: id -> { plugin, ctx } */ private plugins; /** Configuration store (always set in constructor — start() may replace with file-loaded) */ private configStore; /** Track which plugin owns which method: method path -> pluginId */ private pluginMethodOwnership; /** Plugin broadcast subscribers: pluginId -> Set */ private pluginSubscribers; /** Plugin HTTP routes: path → { pluginId, handler } */ private pluginRoutes; constructor(config: GatewayConfig); /** * Walk the methods tree and wrap all handlers as procedures. * Infers full path name (e.g., "tasks:admin:archive") automatically. */ private initializeMethods; /** * Get a method's procedure by path (supports both ":" and "." separators) */ private getMethodProcedure; private initializeTransports; private setupTransportHandlers; /** * Start the gateway (standalone mode only) */ start(): Promise; /** * Stop the gateway */ stop(): Promise; /** * Alias for stop() - useful for embedded mode cleanup */ close(): Promise; /** * Get or create a session with multi-app routing. * Parses session key (e.g., "coding:main") and routes to the correct app. */ session(sessionKey: string): Promise; /** * Close a session and clean up managed state. */ closeSession(sessionKey: string): Promise; /** * Subscribe a client to session events. * Synthetic keys like `$plugin:` route to plugin broadcast subscribers. */ subscribe(sessionKey: string, clientId: string): Promise; /** * Unsubscribe a client from session events. * Handles `$plugin:` synthetic keys. */ unsubscribe(sessionKey: string, clientId: string): void; /** * Send a message to a session and stream events. * Broadcasts events to all subscribers (cross-client push), excluding * the sender who iterates the handle directly. * * @param senderClientId - If provided, this client is excluded from * push broadcasts (they get events through direct handle iteration). */ sendToSession(sessionKey: string, input: SendInput, senderClientId?: string): Promise; /** * Unified execution path: get session, send, iterate events, broadcast. * Used by WS, HTTP, and channel adapter paths. */ private executeSession; /** * Core state management loop: activate session, track message, * iterate events with broadcast, deactivate on completion. * * Both executeSession (yields to caller) and sendToSession * (broadcasts in background) delegate here. The source is any * AsyncIterable — a handle or handle.events. */ private iterateWithBroadcast; /** * Track a user message for state management. * Increments message count and emits session:message event. */ private trackMessage; /** * Create an in-process ClientTransport connected to this gateway. * Returns a ClientTransport for use with createClient(). * * Multiple calls create independent clients sharing the same * underlying LocalGatewayTransport. */ createLocalTransport(): ClientTransport; /** * Get gateway status */ get status(): StatusPayload["gateway"]; /** * Check if running */ get running(): boolean; /** * Get the gateway ID */ get id(): string; /** * Handle an HTTP request (embedded mode). * This is the main entry point when Gateway is embedded in an external framework. * * @param req - Node.js IncomingMessage (or Express/Koa/etc request) * @param res - Node.js ServerResponse (or Express/Koa/etc response) * @returns Promise that resolves when request is handled (may reject on error) * * @example * ```typescript * // Express middleware * app.use("/api", (req, res, next) => { * gateway.handleRequest(req, res).catch(next); * }); * ``` */ handleRequest(req: NodeRequest, res: NodeResponse): Promise; private handleSSE; /** * Clean up channel subscriptions for a disconnected client. */ private cleanupClientChannelSubscriptions; private cleanupPluginSubscriptions; private handleSend; private handleInvoke; private handleSubscribe; private handleAbort; private handleCloseEndpoint; /** * Channel endpoint - handles channel pub/sub operations. */ private handleChannel; /** * Tool confirmation response endpoint. * Handles POST /tool-response with { sessionId, toolUseId, result }. */ private handleToolResponse; /** * Subscribe a client to a session's channel. * Sets up forwarding from core session channel to SSE clients. */ private subscribeToChannel; /** * Forward a channel event to all subscribed clients. */ private forwardChannelEvent; /** * Publish an event to a session's channel. */ private publishToChannel; /** * Check session-level authorization. Returns true if authorized, false if denied (sends 403). * If no authorizeSession callback is configured, always returns true. */ private checkSessionAccess; private parseBody; private handleTransportRequest; private executeMethod; /** * Execute a custom method within Agentick ALS context. */ private executeCustomMethod; private handleSendMethod; private deliverToClient; private getOrCreateBuffer; private sendEventToSubscribers; /** * Direct send handler for HTTP transport. * Returns an async generator that yields events for streaming. * Accepts full Message object to support multimodal content (images, audio, video, docs). * * IMPORTANT: Uses the original sessionId (as provided by client) for events, * not the normalized internal ID. This ensures clients can match events to their sessions. */ private directSend; /** * Respond to a tool confirmation (for HTTP transport). */ private respondToConfirmation; /** * Abort a session's current execution (for HTTP transport). */ private abortSession; /** * Invoke a custom method directly (for HTTP transport). * Called with pre-authenticated user context. */ private invokeMethod; /** * Resolve built-in methods that don't need transport/client context. * Returns undefined if the method is not a built-in. */ private resolveBuiltInMethod; private handleAbortMethod; private handleStatusMethod; private handleHistoryMethod; private handleResetMethod; private handleCloseMethod; private handleAppsMethod; private handleSessionsMethod; private handleSubscribeMethod; private handleUnsubscribeMethod; /** * Send ConnectedMessage to a client after auth succeeds. * Called via onAuthenticated callback from WS/Unix transports. */ private sendConnectedMessage; private handleSchemaMethod; private handleConfigMethod; private handleToolCatalogMethod; private handleToolConfirmMethod; private handleToolDispatchMethod; /** * Register a plugin. Calls plugin.initialize() with a PluginContext. * Throws if a plugin with the same id is already registered. */ use(plugin: GatewayPlugin): Promise; /** * Remove a plugin by id. Calls plugin.destroy() and cleans up its methods. * No-op if plugin id is not found. */ remove(pluginId: string): Promise; /** * Get a registered plugin by id. */ getPlugin(id: string): T | undefined; /** * List all registered plugins with their registered methods. */ listPlugins(): Array<{ id: string; methods: string[]; }>; /** Remove all methods and routes owned by a plugin */ private cleanupPluginRegistrations; /** * Create a PluginContext scoped to a specific plugin. */ private createPluginContext; /** * Match a request path against plugin routes. * Longest prefix wins. Returns undefined if no match. * * @param strippedPath - Path with httpPathPrefix removed (for relative routes) * @param originalPath - Original URL pathname (for absolute routes) */ private matchPluginRoute; /** * Match, authenticate, and dispatch a plugin route. * Single entry point used by both handleRequest (embedded) and HTTP transport. * Returns true if the route was handled (even if auth failed — response already sent). * * When called from handleRequest (embedded), authResult is pre-validated. * When called from HTTP transport, authResult is not provided and auth is * performed inline. */ private dispatchPluginRoute; } export interface Gateway { on(event: K, listener: (payload: GatewayEvents[K]) => void): this; emit(event: K, payload: GatewayEvents[K]): boolean; } /** * Create a gateway instance */ export declare function createGateway(config: GatewayConfig): Gateway; //# sourceMappingURL=gateway.d.ts.map