fix: subagent windows not opening — two cascading bugs

1. handleInit crash: commit cdc822d removed Map initializations for
   teammateTerminals, teammatePanesByName, teams, teamTasks, teammateMap
   from the constructor but left cleanup code that iterates them.
   cleanupAllFloatingWindows() crashed on "not iterable", preventing
   ALL frontend data (sessions, subagents) from loading.

2. claudeSessionId null on recovered sessions: only set inside
   startInteractive(), never in constructor or persisted. After server
   restart, recovered sessions had null claudeSessionId, so the
   hasMatchingTab check always failed → no subagent windows.

Fixes:
- Re-add all 5 missing Map initializations in app.js constructor
- Set _claudeSessionId = this.id in Session constructor (Claudeman
  always passes --session-id to Claude, so they always match)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-13 05:07:51 +01:00
co-authored by Claude Opus 4.6
parent 82a0da035c
commit 7d2519cacb
14 changed files with 168 additions and 83 deletions
+13 -3
View File
@@ -123,7 +123,8 @@ export interface SubagentEvents {
const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude/projects');
const IDLE_TIMEOUT_MS = 30000; // Consider agent idle after 30s of no activity
const POLL_INTERVAL_MS = 1000; // Check for new files every second
const POLL_INTERVAL_MS = 1000; // Base poll interval (lightweight checks)
const FULL_SCAN_EVERY_N_POLLS = 5; // Full directory traversal every 5th poll (5s)
const LIVENESS_CHECK_MS = 10000; // Check if subagent processes are still alive every 10s
const STALE_COMPLETED_MAX_AGE_MS = 60 * 60 * 1000; // Remove completed agents older than 1 hour
const STALE_IDLE_MAX_AGE_MS = 4 * 60 * 60 * 1000; // Remove idle agents older than 4 hours
@@ -168,6 +169,8 @@ export class SubagentWatcher extends EventEmitter {
private pendingToolCalls = new Map<string, Map<string, { toolName: string; timestamp: number }>>();
// Guard to prevent concurrent liveness checks (prevents duplicate completed events)
private _isCheckingLiveness = false;
// Counter for throttling full directory scans (only scan every FULL_SCAN_EVERY_N_POLLS)
private _pollCount = 0;
// Store error handlers for FSWatchers to enable proper cleanup (prevent memory leaks)
private dirWatcherErrorHandlers = new Map<string, (error: Error) => void>();
private fileWatcherErrorHandlers = new Map<string, (error: Error) => void>();
@@ -208,12 +211,18 @@ export class SubagentWatcher extends EventEmitter {
if (this._isRunning) return;
this._isRunning = true;
// Initial scan
// Initial scan (always runs immediately)
this._pollCount = 0;
this.scanForSubagents().catch((err) => this.emit('subagent:error', err as Error));
// Periodic scan for new subagent directories
// Full directory traversal only every FULL_SCAN_EVERY_N_POLLS polls (~5s)
// FSWatchers handle known directories between full scans
this.pollInterval = setInterval(() => {
this.scanForSubagents().catch((err) => this.emit('subagent:error', err as Error));
this._pollCount++;
if (this._pollCount % FULL_SCAN_EVERY_N_POLLS === 0) {
this.scanForSubagents().catch((err) => this.emit('subagent:error', err as Error));
}
}, POLL_INTERVAL_MS);
// Periodic liveness check for active subagents
@@ -338,6 +347,7 @@ export class SubagentWatcher extends EventEmitter {
this.agentInfo.clear();
this.knownSubagentDirs.clear();
this.pendingToolCalls.clear();
this._pollCount = 0;
}
/**