feat: isolate Claude and Codex session sources
Persist platform_source across session creation, transcript ingestion, API query paths, and viewer state so Claude and Codex data can coexist without bleeding into each other. - add platform-source normalization helpers and persist platform_source in sdk_sessions via migration 24 with backfill and indexing - thread platformSource through CLI hooks, transcript processing, context generation, pagination, search routes, SSE payloads, and session management - expose source-aware project catalogs, viewer tabs, context preview selectors, and source badges for observations, prompts, and summaries - start the transcript watcher from the worker for transcript-based clients and preserve platform source during Codex ingestion - auto-start the worker from the MCP server for MCP-only clients and tighten stdio-driven cleanup during shutdown - keep createSDKSession backward compatible with existing custom-title callers while allowing explicit platform source forwarding
This commit is contained in:
@@ -27,7 +27,8 @@ import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { workerHttpRequest } from '../shared/worker-utils.js';
|
||||
import { getWorkerPort, workerHttpRequest } from '../shared/worker-utils.js';
|
||||
import { ensureWorkerStarted } from '../services/worker-service.js';
|
||||
import { searchCodebase, formatSearchResults } from '../services/smart-file-read/search.js';
|
||||
import { parseFile, formatFoldedView, unfoldSymbol } from '../services/smart-file-read/parser.js';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
@@ -144,6 +145,26 @@ async function verifyWorkerConnection(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure Worker is available for Codex and other MCP-only clients.
|
||||
* Claude hooks already start the worker; this path makes Codex turnkey.
|
||||
*/
|
||||
async function ensureWorkerConnection(): Promise<boolean> {
|
||||
if (await verifyWorkerConnection()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.warn('SYSTEM', 'Worker not available, attempting auto-start for MCP client');
|
||||
|
||||
try {
|
||||
const port = getWorkerPort();
|
||||
return await ensureWorkerStarted(port);
|
||||
} catch (error) {
|
||||
logger.error('SYSTEM', 'Worker auto-start failed', undefined, error as Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool definitions with HTTP-based handlers
|
||||
* Minimal descriptions - use help() tool with operation parameter for detailed docs
|
||||
@@ -392,6 +413,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
// Prevents orphaned MCP server processes when Claude Code exits unexpectedly
|
||||
const HEARTBEAT_INTERVAL_MS = 30_000;
|
||||
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let isCleaningUp = false;
|
||||
|
||||
function handleStdioClosed() {
|
||||
cleanup('stdio-closed');
|
||||
}
|
||||
|
||||
function handleStdioError(error: Error) {
|
||||
logger.warn('SYSTEM', 'MCP stdio stream errored, shutting down', {
|
||||
message: error.message
|
||||
});
|
||||
cleanup('stdio-error');
|
||||
}
|
||||
|
||||
function attachStdioLifecycle() {
|
||||
process.stdin.on('end', handleStdioClosed);
|
||||
process.stdin.on('close', handleStdioClosed);
|
||||
process.stdin.on('error', handleStdioError);
|
||||
}
|
||||
|
||||
function detachStdioLifecycle() {
|
||||
process.stdin.off('end', handleStdioClosed);
|
||||
process.stdin.off('close', handleStdioClosed);
|
||||
process.stdin.off('error', handleStdioError);
|
||||
}
|
||||
|
||||
function startParentHeartbeat() {
|
||||
// ppid-based orphan detection only works on Unix
|
||||
@@ -414,9 +459,13 @@ function startParentHeartbeat() {
|
||||
|
||||
// Cleanup function — synchronous to ensure consistent behavior whether called
|
||||
// from signal handlers, heartbeat interval, or awaited in async context
|
||||
function cleanup() {
|
||||
function cleanup(reason: string = 'shutdown') {
|
||||
if (isCleaningUp) return;
|
||||
isCleaningUp = true;
|
||||
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
logger.info('SYSTEM', 'MCP server shutting down');
|
||||
detachStdioLifecycle();
|
||||
logger.info('SYSTEM', 'MCP server shutting down', { reason });
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -428,6 +477,7 @@ process.on('SIGINT', cleanup);
|
||||
async function main() {
|
||||
// Start the MCP server
|
||||
const transport = new StdioServerTransport();
|
||||
attachStdioLifecycle();
|
||||
await server.connect(transport);
|
||||
logger.info('SYSTEM', 'Claude-mem search server started');
|
||||
|
||||
@@ -436,7 +486,7 @@ async function main() {
|
||||
|
||||
// Check Worker availability in background
|
||||
setTimeout(async () => {
|
||||
const workerAvailable = await verifyWorkerConnection();
|
||||
const workerAvailable = await ensureWorkerConnection();
|
||||
if (!workerAvailable) {
|
||||
logger.error('SYSTEM', 'Worker not available', undefined, {});
|
||||
logger.error('SYSTEM', 'Tools will fail until Worker is started');
|
||||
|
||||
Reference in New Issue
Block a user