Fix project isolation: Make loadChatHistory respect active project sessions

- Modified loadChatHistory() to check for active project before fetching all sessions
- When active project exists, use project.sessions instead of fetching from API
- Added detailed console logging to debug session filtering
- This prevents ALL sessions from appearing in every project's sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
uroma
2026-01-22 14:43:05 +00:00
Unverified
parent b82837aa5f
commit 55aafbae9a
6463 changed files with 1115462 additions and 4486 deletions

View File

@@ -6,6 +6,14 @@ const os = require('os');
const { SYSTEM_PROMPT } = require('./system-prompt');
const { extractAllTags, generateOperationSummary, getDyadWriteTags } = require('./tag-parser');
const ResponseProcessor = require('./response-processor');
// ============================================================
// HYBRID APPROACH: Import global EventBus for SSE integration
// ============================================================
const eventBus = require('./event-bus');
// ============================================================
// REAL-TIME MONITORING: Import ChatMonitor
// ============================================================
const chatMonitor = require('./chat-monitor');
class ClaudeCodeService extends EventEmitter {
constructor(vaultPath) {
@@ -101,7 +109,7 @@ class ClaudeCodeService extends EventEmitter {
});
// Emit for real-time updates
this.emit('session-output', {
eventBus.emit('session-output', {
sessionId,
type: 'stdout',
content: text
@@ -118,7 +126,7 @@ class ClaudeCodeService extends EventEmitter {
content: text
});
this.emit('session-output', {
eventBus.emit('session-output', {
sessionId,
type: 'stderr',
content: text
@@ -169,6 +177,12 @@ class ClaudeCodeService extends EventEmitter {
console.log(`[ClaudeService] Sending command to session ${sessionId}:`, command);
// ============================================================
// REAL-TIME MONITORING: Start monitoring for this session
// ============================================================
chatMonitor.startSessionMonitor(sessionId);
chatMonitor.logEvent(sessionId, 'user_message_sent', { command, timestamp: Date.now() });
// Track command in context
session.context.messages.push({
role: 'user',
@@ -186,7 +200,7 @@ class ClaudeCodeService extends EventEmitter {
session.lastActivity = new Date().toISOString();
this.emit('command-sent', {
eventBus.emit('command-sent', {
sessionId,
command
});
@@ -195,36 +209,34 @@ class ClaudeCodeService extends EventEmitter {
const fullCommand = `${SYSTEM_PROMPT}\n\n${command}`;
// Spawn claude in -p (print) mode for this command
const claude = spawn('claude', ['-p', fullCommand], {
// NOTE: --output-format json is REQUIRED for non-interactive mode
const claude = spawn('claude', ['-p', fullCommand, '--output-format', 'json'], {
cwd: session.workingDir,
stdio: ['ignore', 'pipe', 'pipe'], // Explicitly set stdio to get stdout/stderr
env: {
...process.env,
TERM: 'xterm-256color'
}
},
timeout: 120000 // 2 minute timeout to prevent hanging
});
// ============================================================
// REAL-TIME MONITORING: Log Claude spawn
// ============================================================
chatMonitor.logEvent(sessionId, 'claude_spawned', {
pid: claude.pid,
command: 'claude -p <command> --output-format json',
timestamp: Date.now()
});
let output = '';
let stderrOutput = '';
let rawStdout = '';
claude.stdout.on('data', (data) => {
const text = data.toString();
rawStdout += text;
console.log(`[ClaudeService] [${sessionId}] stdout:`, text.substring(0, 100));
output += text;
// Add to output buffer
session.outputBuffer.push({
type: 'stdout',
timestamp: new Date().toISOString(),
content: text
});
// Emit for real-time updates
this.emit('session-output', {
sessionId,
type: 'stdout',
content: text
});
});
claude.stderr.on('data', (data) => {
@@ -238,7 +250,7 @@ class ClaudeCodeService extends EventEmitter {
content: text
});
this.emit('session-output', {
eventBus.emit('session-output', {
sessionId,
type: 'stderr',
content: text
@@ -248,6 +260,88 @@ class ClaudeCodeService extends EventEmitter {
claude.on('close', (code) => {
console.log(`[ClaudeService] [${sessionId}] Command completed with exit code ${code}`);
// Parse JSON output from Claude CLI
// The --output-format json flag returns: { "type": "result", "result": "...", ... }
try {
const jsonOutput = JSON.parse(rawStdout.trim());
if (jsonOutput.type === 'result' && jsonOutput.result) {
output = jsonOutput.result;
// ============================================================
// REAL-TIME MONITORING: Log JSON parsing success
// ============================================================
chatMonitor.logEvent(sessionId, 'json_parsed', {
resultLength: output.length,
timestamp: Date.now()
});
// Add to output buffer
session.outputBuffer.push({
type: 'stdout',
timestamp: new Date().toISOString(),
content: output
});
// Emit for real-time updates
eventBus.emit('session-output', {
sessionId,
type: 'stdout',
content: output
});
// ============================================================
// REAL-TIME MONITORING: Log SSE emit and AI response
// ============================================================
chatMonitor.logEvent(sessionId, 'sse_emit', {
eventType: 'session-output',
contentLength: output.length,
timestamp: Date.now()
});
chatMonitor.logEvent(sessionId, 'ai_response', {
content: output.substring(0, 200),
fullLength: output.length,
timestamp: Date.now()
});
console.log(`[ClaudeService] [${sessionId}] Parsed JSON output, result length: ${output.length}`);
} else if (jsonOutput.type === 'error') {
// Handle error responses
const errorMsg = jsonOutput.error || jsonOutput.message || 'Unknown error';
console.error(`[ClaudeService] [${sessionId}] Claude CLI error:`, errorMsg);
eventBus.emit('session-output', {
sessionId,
type: 'stderr',
content: `Error: ${errorMsg}`
});
} else {
// Fallback to raw output if JSON structure is unexpected
console.warn(`[ClaudeService] [${sessionId}] Unexpected JSON structure:`, jsonOutput);
output = rawStdout;
}
} catch (parseError) {
// If parsing fails, treat as raw text output
console.warn(`[ClaudeService] [${sessionId}] Failed to parse JSON output, using raw:`, parseError.message);
output = rawStdout;
if (output.trim()) {
// Emit raw output as fallback
session.outputBuffer.push({
type: 'stdout',
timestamp: new Date().toISOString(),
content: output
});
eventBus.emit('session-output', {
sessionId,
type: 'stdout',
content: output
});
}
}
// Add assistant response to context
if (output.trim()) {
session.context.messages.push({
@@ -262,7 +356,7 @@ class ClaudeCodeService extends EventEmitter {
if (tags.writes.length > 0 || tags.renames.length > 0 || tags.deletes.length > 0 || tags.dependencies.length > 0) {
const operations = generateOperationSummary(tags);
this.emit('operations-detected', {
eventBus.emit('operations-detected', {
sessionId,
response: output,
tags,
@@ -272,7 +366,7 @@ class ClaudeCodeService extends EventEmitter {
console.log(`[ClaudeService] Detected ${operations.length} operations requiring approval`);
}
this.emit('command-complete', {
eventBus.emit('command-complete', {
sessionId,
exitCode: code,
output
@@ -284,7 +378,7 @@ class ClaudeCodeService extends EventEmitter {
claude.on('error', (error) => {
console.error(`[ClaudeService] [${sessionId}] Process error:`, error);
this.emit('session-error', {
eventBus.emit('session-error', {
sessionId,
error: error.message
});
@@ -316,7 +410,7 @@ class ClaudeCodeService extends EventEmitter {
});
// Emit approval-request event for WebSocket to handle
this.emit('approval-request', {
eventBus.emit('approval-request', {
sessionId,
command,
explanation
@@ -478,15 +572,17 @@ class ClaudeCodeService extends EventEmitter {
/**
* List all sessions
* Sessions in memory are considered active/running even without a process
* because processes are spawned on-demand when commands are sent
*/
listSessions() {
return Array.from(this.sessions.values()).map(session => {
const metadata = this.calculateSessionMetadata(session);
// FIX: Only mark as running if process is actually alive
const isRunning = session.status === 'running' &&
session.process &&
!session.process.killed;
// FIX: Sessions in memory are considered active
// In the new architecture, processes are spawned on-demand
// A session is "running" if it exists in memory and hasn't been stopped
const isRunning = session.status === 'running';
return {
id: session.id,
@@ -777,7 +873,7 @@ class ClaudeCodeService extends EventEmitter {
}
);
this.emit('operations-executed', {
eventBus.emit('operations-executed', {
sessionId,
results
});
@@ -785,7 +881,7 @@ class ClaudeCodeService extends EventEmitter {
return results;
} catch (error) {
console.error(`[ClaudeService] Error executing operations:`, error);
this.emit('operations-error', {
eventBus.emit('operations-error', {
sessionId,
error: error.message
});