mirror of
https://github.com/crabwise-ai/crabwalk.git
synced 2026-08-14 00:57:52 +00:00
fix(monitor): improve subagent spawn inference and minimap sizing (#22)
* refactor(clawdbot): update spawn inference logic - Replaced parent session activity tracking with a pending spawns array to record Task tool calls. - Adjusted the spawn inference window to 10 seconds, ensuring accurate tracking of recent Task calls. - Enhanced the inferSpawnedBy function to utilize the new pending spawns structure for improved session mapping. - Removed obsolete parent session activity tracking code for cleaner implementation. * fix(clawdbot): normalize tool name handling in action tracking - Updated the action tracking logic to convert tool names to lowercase for consistent comparison, ensuring accurate detection of 'Task' tool calls in both addAction and hydrateFromServer functions. - Enhanced event parsing to process tool events more effectively, improving overall event handling and session state management. * refactor(clawdbot): enhance spawn inference with parent session activity tracking - Introduced a new Map to track recent parent session activity, providing a fallback for spawn inference when Task tool calls are not available. - Updated the recordPendingSpawn and inferSpawnedBy functions to utilize this new tracking mechanism, improving the accuracy of session mapping. - Enhanced the addAction and hydrateFromServer functions to incorporate parent session activity tracking, ensuring comprehensive spawn history management. - Cleared parent session activity during collection resets to maintain data integrity. * refactor(clawdbot): improve parent session action tracking for spawn inference - Replaced the previous parent session activity tracking with a new structure to maintain a history of recent actions, allowing for more precise spawn inference. - Updated the trackParentAction function to log actions with timestamps and manage a maximum history length. - Modified the inferSpawnedBy function to utilize the new action history for better accuracy in linking subagents to their parent sessions. - Adjusted the addAction and hydrateFromServer functions to reflect the new tracking mechanism, ensuring comprehensive session mapping. - Cleared the action history during collection resets to maintain data integrity. * style(monitor): adjust ActionGraph component dimensions - Updated the ActionGraphInner component to include width and height styles, enhancing the layout and visual consistency of the graph representation.
This commit is contained in:
@@ -791,7 +791,7 @@ function ActionGraphInner({
|
||||
}}
|
||||
maskColor="rgba(10, 10, 15, 0.8)"
|
||||
className="bg-shell-900! border-shell-700!"
|
||||
style={{ backgroundColor: '#0a0a0f' }}
|
||||
style={{ backgroundColor: '#0a0a0f', width: 100, height: 75 }}
|
||||
/>
|
||||
</ReactFlow>
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,13 @@ import {
|
||||
// Track runId → sessionKey mapping (learned from chat events)
|
||||
const runSessionMap = new Map<string, string>()
|
||||
|
||||
// Track recent activity on parent (non-subagent) sessions for spawn inference
|
||||
// Maps sessionKey → lastActivityTimestamp
|
||||
const parentSessionActivity = new Map<string, number>()
|
||||
// Track recent parent session actions with precise timestamps
|
||||
// Stores the last few action timestamps per parent session
|
||||
const parentActionHistory = new Map<string, number[]>()
|
||||
const MAX_ACTION_HISTORY = 10
|
||||
|
||||
// Time window for spawn inference - parent must have been active within this window
|
||||
const SPAWN_INFERENCE_WINDOW_MS = 5000
|
||||
// Time window for spawn inference
|
||||
const SPAWN_INFERENCE_WINDOW_MS = 10000
|
||||
|
||||
function isSubagentSession(key: string): boolean {
|
||||
return key.includes('subagent')
|
||||
@@ -27,31 +28,58 @@ function isParentSession(key: string): boolean {
|
||||
return !isSubagentSession(key) && !key.includes('lifecycle')
|
||||
}
|
||||
|
||||
// Infer which parent session spawned this subagent based on recent activity
|
||||
// Track an action on a parent session with its timestamp
|
||||
function trackParentAction(sessionKey: string, timestamp?: number) {
|
||||
if (!isParentSession(sessionKey)) return
|
||||
const ts = timestamp ?? Date.now()
|
||||
|
||||
let history = parentActionHistory.get(sessionKey)
|
||||
if (!history) {
|
||||
history = []
|
||||
parentActionHistory.set(sessionKey, history)
|
||||
}
|
||||
|
||||
history.push(ts)
|
||||
|
||||
// Keep only recent entries
|
||||
if (history.length > MAX_ACTION_HISTORY) {
|
||||
history.shift()
|
||||
}
|
||||
}
|
||||
|
||||
// Infer which parent session spawned this subagent
|
||||
// Finds the parent with the most recent action before the subagent's timestamp
|
||||
function inferSpawnedBy(subagentKey: string, timestamp?: number): string | undefined {
|
||||
if (!isSubagentSession(subagentKey)) return undefined
|
||||
|
||||
const now = timestamp ?? Date.now()
|
||||
const subagentTime = timestamp ?? Date.now()
|
||||
const cutoff = subagentTime - SPAWN_INFERENCE_WINDOW_MS
|
||||
|
||||
let bestParent: string | undefined
|
||||
let bestTime = 0
|
||||
|
||||
for (const [parentKey, activityTime] of parentSessionActivity) {
|
||||
// Must be within inference window
|
||||
if (now - activityTime > SPAWN_INFERENCE_WINDOW_MS) continue
|
||||
// Pick most recently active parent
|
||||
if (activityTime > bestTime) {
|
||||
bestTime = activityTime
|
||||
bestParent = parentKey
|
||||
for (const [parentKey, history] of parentActionHistory) {
|
||||
// Find the most recent action from this parent that's before the subagent time
|
||||
for (let i = history.length - 1; i >= 0; i--) {
|
||||
const actionTime = history[i]!
|
||||
// Must be before subagent appeared and within window
|
||||
if (actionTime <= subagentTime && actionTime >= cutoff) {
|
||||
if (actionTime > bestTime) {
|
||||
bestTime = actionTime
|
||||
bestParent = parentKey
|
||||
}
|
||||
break // Found the most recent valid action for this parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestParent
|
||||
}
|
||||
if (bestParent) {
|
||||
console.log(`[spawn] linked ${subagentKey} to ${bestParent} (action ${subagentTime - bestTime}ms before)`)
|
||||
} else {
|
||||
console.log(`[spawn] could not infer parent for ${subagentKey}`)
|
||||
}
|
||||
|
||||
// Track activity on a parent session
|
||||
function trackParentActivity(sessionKey: string, timestamp?: number) {
|
||||
if (!isParentSession(sessionKey)) return
|
||||
parentSessionActivity.set(sessionKey, timestamp ?? Date.now())
|
||||
return bestParent
|
||||
}
|
||||
|
||||
export const sessionsCollection = createCollection(
|
||||
@@ -164,11 +192,6 @@ function createPlaceholderExec(event: MonitorExecEvent, sessionKey?: string): Mo
|
||||
|
||||
// Helper to update or insert session
|
||||
export function upsertSession(session: MonitorSession) {
|
||||
// Track activity on parent sessions
|
||||
if (isParentSession(session.key)) {
|
||||
trackParentActivity(session.key, session.lastActivityAt)
|
||||
}
|
||||
|
||||
const existing = sessionsCollection.state.get(session.key)
|
||||
|
||||
if (existing) {
|
||||
@@ -207,9 +230,9 @@ export function addAction(action: MonitorAction) {
|
||||
backfillExecSessionKey(action.runId, action.sessionKey)
|
||||
}
|
||||
|
||||
// Track activity on parent sessions for spawn inference
|
||||
// Track parent session actions for spawn inference
|
||||
if (isParentSession(action.sessionKey)) {
|
||||
trackParentActivity(action.sessionKey, action.timestamp)
|
||||
trackParentAction(action.sessionKey, action.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,12 +393,6 @@ export function updateSessionStatus(
|
||||
status: MonitorSession['status']
|
||||
) {
|
||||
const now = Date.now()
|
||||
|
||||
// Track activity on parent sessions
|
||||
if (isParentSession(key)) {
|
||||
trackParentActivity(key, now)
|
||||
}
|
||||
|
||||
const session = sessionsCollection.state.get(key)
|
||||
if (session) {
|
||||
sessionsCollection.update(key, (draft) => {
|
||||
@@ -412,7 +429,7 @@ export function updateSession(key: string, update: Partial<MonitorSession>) {
|
||||
// Clear all data
|
||||
export function clearCollections() {
|
||||
runSessionMap.clear()
|
||||
parentSessionActivity.clear()
|
||||
parentActionHistory.clear()
|
||||
for (const session of sessionsCollection.state.values()) {
|
||||
sessionsCollection.delete(session.key)
|
||||
}
|
||||
@@ -478,23 +495,24 @@ export function hydrateFromServer(
|
||||
// First clear existing data
|
||||
clearCollections()
|
||||
|
||||
// Replay actions first to build parent activity history
|
||||
// Sort actions by timestamp for replay
|
||||
const sortedActions = [...actions].sort((a, b) => a.timestamp - b.timestamp)
|
||||
|
||||
// First pass: build parent action history for spawn inference
|
||||
for (const action of sortedActions) {
|
||||
// Track parent activity without inserting actions yet
|
||||
if (action.sessionKey && isParentSession(action.sessionKey)) {
|
||||
trackParentActivity(action.sessionKey, action.timestamp)
|
||||
trackParentAction(action.sessionKey, action.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
// Also track parent sessions by their lastActivityAt
|
||||
for (const session of sessions) {
|
||||
if (isParentSession(session.key)) {
|
||||
trackParentActivity(session.key, session.lastActivityAt)
|
||||
trackParentAction(session.key, session.lastActivityAt)
|
||||
}
|
||||
}
|
||||
|
||||
// Now insert all sessions - subagents will get inferred spawnedBy
|
||||
// Insert all sessions - subagents will get inferred spawnedBy from Task tool calls
|
||||
for (const session of sessions) {
|
||||
if (isSubagentSession(session.key)) {
|
||||
const spawnedBy = session.spawnedBy || inferSpawnedBy(session.key, session.lastActivityAt)
|
||||
|
||||
@@ -203,6 +203,18 @@ export function parseEventFrame(
|
||||
}
|
||||
}
|
||||
|
||||
// Process tool events (tool_use, tool_result)
|
||||
if (agentEvent.data?.type === 'tool_use' || agentEvent.data?.type === 'tool_result') {
|
||||
return {
|
||||
action: agentEventToAction(agentEvent),
|
||||
session: agentEvent.sessionKey ? {
|
||||
key: agentEvent.sessionKey,
|
||||
status: 'thinking',
|
||||
lastActivityAt: Date.now(),
|
||||
} : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user