()
| 553 | // at end of run; scanning only the delta (response.newEvents) is O(new). |
| 554 | let cachedReviewContent: string | null = null; |
| 555 | const poll = async (): Promise<void> => { |
| 556 | if (!isRunning) return; |
| 557 | try { |
| 558 | const appState = context.getAppState(); |
| 559 | const task = appState.tasks?.[taskId] as RemoteAgentTaskState | undefined; |
| 560 | if (!task || task.status !== 'running') { |
| 561 | // Task was killed externally (TaskStopTool) or already terminal. |
| 562 | // Session left alive so the remote URL stays valid — the run_hunt.sh |
| 563 | // post_stage() calls land as assistant events there, and the user may |
| 564 | // want to revisit them after closing the terminal. TTL reaps it. |
| 565 | return; |
| 566 | } |
| 567 | const response = await pollRemoteSessionEvents(task.sessionId, lastEventId); |
| 568 | lastEventId = response.lastEventId; |
| 569 | const logGrew = response.newEvents.length > 0; |
| 570 | if (logGrew) { |
| 571 | accumulatedLog = [...accumulatedLog, ...response.newEvents]; |
| 572 | const deltaText = response.newEvents.map(msg => { |
| 573 | if (msg.type === 'assistant') { |
| 574 | return msg.message.content.filter(block => block.type === 'text').map(block => 'text' in block ? block.text : '').join('\n'); |
| 575 | } |
| 576 | return jsonStringify(msg); |
| 577 | }).join('\n'); |
| 578 | if (deltaText) { |
| 579 | appendTaskOutput(taskId, deltaText + '\n'); |
| 580 | } |
| 581 | } |
| 582 | if (response.sessionStatus === 'archived') { |
| 583 | updateTaskState<RemoteAgentTaskState>(taskId, context.setAppState, t => t.status === 'running' ? { |
| 584 | ...t, |
| 585 | status: 'completed', |
| 586 | endTime: Date.now() |
| 587 | } : t); |
| 588 | enqueueRemoteNotification(taskId, task.title, 'completed', context.setAppState, task.toolUseId); |
| 589 | void evictTaskOutput(taskId); |
| 590 | void removeRemoteAgentMetadata(taskId); |
| 591 | return; |
| 592 | } |
| 593 | const checker = completionCheckers.get(task.remoteTaskType); |
| 594 | if (checker) { |
| 595 | const completionResult = await checker(task.remoteTaskMetadata); |
| 596 | if (completionResult !== null) { |
| 597 | updateTaskState<RemoteAgentTaskState>(taskId, context.setAppState, t => t.status === 'running' ? { |
| 598 | ...t, |
| 599 | status: 'completed', |
| 600 | endTime: Date.now() |
| 601 | } : t); |
| 602 | enqueueRemoteNotification(taskId, completionResult, 'completed', context.setAppState, task.toolUseId); |
| 603 | void evictTaskOutput(taskId); |
| 604 | void removeRemoteAgentMetadata(taskId); |
| 605 | return; |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | // Ultraplan: result(success) fires after every CCR turn, so it must not |
| 610 | // drive completion — startDetachedPoll owns that via ExitPlanMode scan. |
| 611 | // Long-running monitors (autofix-pr) emit result per notification cycle, |
| 612 | // so the same skip applies. |
no test coverage detected