/** * MCP session registry — in-memory tracking of active transports, * thread ownership, supersede semantics, and close-flush handlers. */ /** Associate a transport session id with its per-process spawn token. */ export declare function associateSessionToken(mcpSessionId: string, spawnToken: string): void; /** Check if a session has been superseded by a newer session for its thread. */ export declare function isSessionSuperseded(mcpSessionId: string | undefined, threadId?: number): boolean; /** * Reconcile thread ownership for an incoming tool call and report whether the * caller may proceed. * * Returns true if the caller is superseded (a newer session owns the thread and * this one must stop). Returns false if the caller is the active owner — * claiming ownership when the thread is currently unowned. * * Identity is the per-process spawn token (ownerKey), which is STABLE across * transport reconnects. Therefore: * - A reconnect of the owning process resolves to the SAME key → allowed, * with no ownership change (fixes false-supersede-on-reconnect). * - A genuinely different process (distinct spawn token) is REJECTED while a * token-bearing owner holds the thread — ownership only transfers via * start_session (setThreadOwnerSession), which supersedes the prior owner. * This makes zombie / duplicate processes reliably detectable. * - When token info is missing on either side (legacy/STDIO), fall back to the * previous adoption behavior so those paths keep working. * * The owner key is thread-scoped (`tok:#`), so a single process * (shared token) legitimately owning MANY threads is fully supported: each * thread carries its own key and its own ownership row. A forged `threadId` still * cannot hijack another thread — it would build a NEW key (its own token, the * target thread), collide with that thread's real token-bearing owner, and be * rejected below as a zombie/duplicate — so no separate cross-thread guard is * needed (and a token-only one would misfire on the shared-token reality). */ export declare function reconcileThreadOwnership(mcpSessionId: string | undefined, threadId: number): boolean; /** Mark a session as the sole owner for a thread. Old owners are superseded. */ export declare function setThreadOwnerSession(threadId: number, mcpSessionId: string): void; export declare function registerMcpSession(threadId: number, mcpSessionId: string, closeTransport: () => void): void; export declare function getThreadIdForMcpSession(mcpSessionId: string): number | undefined; /** * DIAGNOSTIC (read-only): number of MCP transports currently registered for a * thread. 0 means the agent is no longer MCP-connected to this server (orphaned * / transport gone). Used by the keeper's stuck-thread forensic snapshot. */ export declare function getThreadSessionCount(threadId: number): number; export declare function registerSessionCloseFlush(mcpSessionId: string, flush: () => Promise): void; /** Run and remove a session's close-flush handler. No-op if none registered. */ export declare function runSessionCloseFlush(mcpSessionId: string): Promise | undefined; /** Drop a session's close-flush handler without running it (non-forward closes). */ export declare function clearSessionCloseFlush(mcpSessionId: string): void; export declare function expectMcpSessionClose(mcpSessionId: string): void; export declare function consumeExpectedMcpSessionClose(mcpSessionId: string): boolean; export declare function unregisterMcpSession(mcpSessionId: string): void; /** * Close all MCP sessions for a thread EXCEPT the current one. * Purges orphaned sessions from before a server restart. * Returns the number of sessions purged. */ export declare function purgeOtherSessions(threadId: number, keepMcpSessionId?: string): number; /** * Close and unregister ALL MCP transports for a thread being retired * (worker decommission / TTL cleanup / backstop reap). * * Root-cause teardown for zombie agents: an agent process keeps its Node event * loop alive as long as it holds an open MCP socket to this server, so it never * exits on its own — and a server cannot reliably force-kill its OWN child on * Windows (parent→child OpenProcess returns EPERM). But the server DOES own the * server side of that socket. Closing the transport sends the client a FIN, its * event loop drains, and the agent process exits by itself with code 0. * * Marks each close as expected so the http-server onclose handler treats it as a * deliberate teardown (no kill-to-reconnect, no false zombie detection). * Returns the number of transports closed. */ export declare function closeThreadSessions(threadId: number): number; //# sourceMappingURL=mcp-registry.d.ts.map