()
| 297 | } |
| 298 | |
| 299 | export function useMasterMonitor(): void { |
| 300 | const role = useAppState(s => getPipeIpc(s).role) |
| 301 | const setAppState = useSetAppState() |
| 302 | const registryVersion = useSyncExternalStore( |
| 303 | subscribeToSlaveClientRegistry, |
| 304 | getSlaveClientRegistryVersion, |
| 305 | getSlaveClientRegistryVersion, |
| 306 | ) |
| 307 | |
| 308 | useEffect(() => { |
| 309 | if (role !== 'master' && _slaveClients.size === 0) return |
| 310 | |
| 311 | // Set up listeners for each connected slave client |
| 312 | const cleanups: (() => void)[] = [] |
| 313 | |
| 314 | for (const [slaveName, client] of _slaveClients.entries()) { |
| 315 | const handler = (msg: PipeMessage) => { |
| 316 | // Only record relevant message types |
| 317 | if (!isMonitoredPipeEntryType(msg.type)) { |
| 318 | return |
| 319 | } |
| 320 | |
| 321 | // Mute gate (second gate, same helper as attachPipeEntryEmitter) |
| 322 | if (shouldDropMutedMessage(slaveName, msg.type)) { |
| 323 | return |
| 324 | } |
| 325 | |
| 326 | // Clear /send override when slave turn completes |
| 327 | if ( |
| 328 | (msg.type === 'done' || msg.type === 'error') && |
| 329 | hasSendOverride(slaveName) |
| 330 | ) { |
| 331 | removeSendOverride(slaveName) |
| 332 | } |
| 333 | |
| 334 | const entry = pipeMessageToSessionEntry(slaveName, msg) |
| 335 | |
| 336 | setAppState(prev => { |
| 337 | const slave = getPipeIpc(prev).slaves[slaveName] |
| 338 | if (!slave) return prev |
| 339 | |
| 340 | const newStatus = |
| 341 | msg.type === 'done' || msg.type === 'error' |
| 342 | ? 'idle' |
| 343 | : msg.type === 'prompt' |
| 344 | ? 'busy' |
| 345 | : slave.status |
| 346 | |
| 347 | return { |
| 348 | ...prev, |
| 349 | pipeIpc: { |
| 350 | ...getPipeIpc(prev), |
| 351 | slaves: { |
| 352 | ...getPipeIpc(prev).slaves, |
| 353 | [slaveName]: applyPipeEntryToSlaveState( |
| 354 | { |
| 355 | ...slave, |
| 356 | status: newStatus, |
no test coverage detected