mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 20:49:41 +02:00
fix: match subagents to sessions by claudeSessionId instead of newest heuristic
When multiple Claudeman sessions share the same workingDir, subagents were incorrectly attaching to the newest session instead of the actual parent. Now uses direct claudeSessionId matching for reliable parent detection. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ When user says "COM":
|
||||
1. Increment version in BOTH `package.json` AND `CLAUDE.md`
|
||||
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
||||
|
||||
**Version**: 0.1422 (must match `package.json`)
|
||||
**Version**: 0.1423 (must match `package.json`)
|
||||
|
||||
## Project Overview
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claudeman",
|
||||
"version": "0.1422",
|
||||
"version": "0.1423",
|
||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
+31
-9
@@ -9247,7 +9247,23 @@ class ClaudemanApp {
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: Find all sessions that match by workingDir
|
||||
// Strategy 2: Match by claudeSessionId (most reliable)
|
||||
// The subagent's sessionId is the Claude session that spawned it
|
||||
// Match this to the Claudeman session's claudeSessionId
|
||||
if (agent.sessionId) {
|
||||
for (const [sessionId, session] of this.sessions) {
|
||||
if (session.claudeSessionId === agent.sessionId) {
|
||||
agent.parentSessionId = sessionId;
|
||||
agent.parentSessionName = this.getSessionName(session);
|
||||
this.subagents.set(agentId, agent);
|
||||
this.updateSubagentWindowParent(agentId);
|
||||
this.updateSubagentWindowVisibility();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: Fall back to workingDir matching (for sessions without claudeSessionId yet)
|
||||
const matchingSessions = [];
|
||||
for (const [sessionId, session] of this.sessions) {
|
||||
try {
|
||||
@@ -9272,14 +9288,20 @@ class ClaudemanApp {
|
||||
let chosen = matchingSessions[0];
|
||||
|
||||
if (matchingSessions.length > 1) {
|
||||
// Multiple sessions with same workingDir - use heuristics to pick the right one
|
||||
// Prefer the most recently created session (newest session likely spawned the subagent)
|
||||
matchingSessions.sort((a, b) => {
|
||||
const aTime = new Date(a.session.createdAt || 0).getTime();
|
||||
const bTime = new Date(b.session.createdAt || 0).getTime();
|
||||
return bTime - aTime; // Newest first
|
||||
});
|
||||
chosen = matchingSessions[0];
|
||||
// Multiple sessions with same workingDir - prefer session with matching claudeSessionId
|
||||
// If no match, fall back to oldest session (first created is more likely the original)
|
||||
const byClaudeSession = matchingSessions.find(m => m.session.claudeSessionId === agent.sessionId);
|
||||
if (byClaudeSession) {
|
||||
chosen = byClaudeSession;
|
||||
} else {
|
||||
// Fall back to oldest session (not newest - oldest is more likely the original workspace)
|
||||
matchingSessions.sort((a, b) => {
|
||||
const aTime = new Date(a.session.createdAt || 0).getTime();
|
||||
const bTime = new Date(b.session.createdAt || 0).getTime();
|
||||
return aTime - bTime; // Oldest first
|
||||
});
|
||||
chosen = matchingSessions[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the parent
|
||||
|
||||
+95
-30
@@ -2368,53 +2368,91 @@ body {
|
||||
}
|
||||
|
||||
/* Run Summary Modal */
|
||||
|
||||
/* Summary tab scrollable container */
|
||||
#summary-tab {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(70vh - 120px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#summary-tab .run-summary-stats {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#summary-tab .run-summary-filters {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#summary-tab .run-summary-timeline {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
#summary-tab .run-summary-footer {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.run-summary-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.run-summary-stats .stat-card {
|
||||
background: var(--bg-hover);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.02) 0%, rgba(255, 255, 255, 0.04) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0.625rem;
|
||||
text-align: center;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.run-summary-stats .stat-card:hover {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.05) 100%);
|
||||
}
|
||||
|
||||
.run-summary-stats .stat-value {
|
||||
font-size: 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
color: var(--text);
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.run-summary-stats .stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 0.125rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.run-summary-filters {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 0.375rem 0.75rem;
|
||||
background: var(--bg-hover);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.25rem 0.625rem;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 100px;
|
||||
color: var(--text-dim);
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.7rem;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.filter-btn:hover {
|
||||
background: var(--bg-card);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@@ -2427,17 +2465,41 @@ body {
|
||||
.run-summary-timeline {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
padding-right: 0.25rem;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
|
||||
}
|
||||
|
||||
.run-summary-timeline::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.run-summary-timeline::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.run-summary-timeline::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.run-summary-timeline::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.timeline-event {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--bg-hover);
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.625rem;
|
||||
margin-bottom: 0.375rem;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid var(--border);
|
||||
border-left: 2px solid var(--border);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.timeline-event:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.timeline-event.event-success {
|
||||
@@ -2457,8 +2519,9 @@ body {
|
||||
}
|
||||
|
||||
.event-icon {
|
||||
font-size: 1.1rem;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.event-content {
|
||||
@@ -2475,19 +2538,21 @@ body {
|
||||
|
||||
.event-title {
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.event-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
|
||||
.event-details {
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-dim);
|
||||
margin-top: 0.25rem;
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
.run-summary-session-info {
|
||||
|
||||
Reference in New Issue
Block a user