@parhelia/core 0.4.12818 → 0.4.12825

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/config/config.js +3 -0
  2. package/dist/config/config.js.map +1 -1
  3. package/dist/editor/ai/AgentStatusBadge.js +11 -9
  4. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  5. package/dist/editor/ai/Agents.js +2 -2
  6. package/dist/editor/ai/Agents.js.map +1 -1
  7. package/dist/editor/ai/agentDiagnostics.d.ts +1 -1
  8. package/dist/editor/ai/agentDiagnostics.js +8 -0
  9. package/dist/editor/ai/agentDiagnostics.js.map +1 -1
  10. package/dist/editor/ai/terminal/agentSessionSnapshot.d.ts +15 -0
  11. package/dist/editor/ai/terminal/agentSessionSnapshot.js +103 -3
  12. package/dist/editor/ai/terminal/agentSessionSnapshot.js.map +1 -1
  13. package/dist/editor/ai/terminal/agentSessionState.js +5 -1
  14. package/dist/editor/ai/terminal/agentSessionState.js.map +1 -1
  15. package/dist/editor/ai/terminal/components/AgentActivityOverviewPanel.js +5 -5
  16. package/dist/editor/ai/terminal/components/AgentActivityOverviewPanel.js.map +1 -1
  17. package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js +1 -1
  18. package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js.map +1 -1
  19. package/dist/editor/ai/terminal/components/AgentSkillSettingsSection.js +2 -2
  20. package/dist/editor/ai/terminal/components/AgentSkillSettingsSection.js.map +1 -1
  21. package/dist/editor/ai/terminal/components/AiResponseMessage.js +8 -7
  22. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  23. package/dist/editor/ai/terminal/components/ContextInfoBar.js +1 -1
  24. package/dist/editor/ai/terminal/components/ContextInfoBar.js.map +1 -1
  25. package/dist/editor/ai/terminal/useAgentEditorContext.js +13 -3
  26. package/dist/editor/ai/terminal/useAgentEditorContext.js.map +1 -1
  27. package/dist/editor/ai/terminal/useAgentOperationHistoryLoader.js +5 -1
  28. package/dist/editor/ai/terminal/useAgentOperationHistoryLoader.js.map +1 -1
  29. package/dist/editor/ai/terminal/useAgentOperationSocketHandler.js +33 -1
  30. package/dist/editor/ai/terminal/useAgentOperationSocketHandler.js.map +1 -1
  31. package/dist/editor/ai/terminal/useAgentServerReconciler.js +11 -0
  32. package/dist/editor/ai/terminal/useAgentServerReconciler.js.map +1 -1
  33. package/dist/editor/ai/terminal/useAgentSubmitHandlers.d.ts +1 -0
  34. package/dist/editor/ai/terminal/useAgentSubmitHandlers.js +6 -2
  35. package/dist/editor/ai/terminal/useAgentSubmitHandlers.js.map +1 -1
  36. package/dist/editor/ai/terminal/useAgentTerminalController.js +4 -0
  37. package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
  38. package/dist/editor/bridge/protocol.d.ts +5 -0
  39. package/dist/editor/bridge/protocol.js.map +1 -1
  40. package/dist/editor/client/EditorShell.js +4 -3
  41. package/dist/editor/client/EditorShell.js.map +1 -1
  42. package/dist/editor/client/hooks/useSocketMessageHandler.js +3 -1
  43. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  44. package/dist/editor/client/operations.js +34 -2
  45. package/dist/editor/client/operations.js.map +1 -1
  46. package/dist/editor/commands/commands.d.ts +6 -0
  47. package/dist/editor/commands/commands.js.map +1 -1
  48. package/dist/editor/field-types/RichTextEditorComponent.js +10 -1
  49. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  50. package/dist/editor/field-types/richtext/components/ReactSlate.js +8 -2
  51. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  52. package/dist/editor/field-types/useFormFieldCaretPresence.js +1 -0
  53. package/dist/editor/field-types/useFormFieldCaretPresence.js.map +1 -1
  54. package/dist/editor/find-replace/FindReplacePanel.d.ts +11 -0
  55. package/dist/editor/find-replace/FindReplacePanel.js +311 -0
  56. package/dist/editor/find-replace/FindReplacePanel.js.map +1 -0
  57. package/dist/editor/find-replace/findReplaceCommand.d.ts +8 -0
  58. package/dist/editor/find-replace/findReplaceCommand.js +25 -0
  59. package/dist/editor/find-replace/findReplaceCommand.js.map +1 -0
  60. package/dist/editor/find-replace/findReplaceMatches.d.ts +42 -0
  61. package/dist/editor/find-replace/findReplaceMatches.js +66 -0
  62. package/dist/editor/find-replace/findReplaceMatches.js.map +1 -0
  63. package/dist/editor/find-replace/findReplaceStore.d.ts +30 -0
  64. package/dist/editor/find-replace/findReplaceStore.js +61 -0
  65. package/dist/editor/find-replace/findReplaceStore.js.map +1 -0
  66. package/dist/editor/find-replace/findReplaceTargets.d.ts +49 -0
  67. package/dist/editor/find-replace/findReplaceTargets.js +110 -0
  68. package/dist/editor/find-replace/findReplaceTargets.js.map +1 -0
  69. package/dist/editor/find-replace/findReplaceText.d.ts +50 -0
  70. package/dist/editor/find-replace/findReplaceText.js +189 -0
  71. package/dist/editor/find-replace/findReplaceText.js.map +1 -0
  72. package/dist/editor/menubar/ActiveUsers.js +1 -1
  73. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  74. package/dist/editor/menubar/ToolbarFactory.js +9 -1
  75. package/dist/editor/menubar/ToolbarFactory.js.map +1 -1
  76. package/dist/editor/menubar/VersionSelector.js +2 -2
  77. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  78. package/dist/editor/menubar/toolbar-sections/FindReplaceControls.d.ts +6 -0
  79. package/dist/editor/menubar/toolbar-sections/FindReplaceControls.js +31 -0
  80. package/dist/editor/menubar/toolbar-sections/FindReplaceControls.js.map +1 -0
  81. package/dist/editor/menubar/toolbar-sections/index.d.ts +1 -0
  82. package/dist/editor/menubar/toolbar-sections/index.js +1 -0
  83. package/dist/editor/menubar/toolbar-sections/index.js.map +1 -1
  84. package/dist/editor/page-editor-chrome/FieldActionIndicator.js +8 -4
  85. package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
  86. package/dist/editor/page-editor-chrome/FindReplaceHighlightings.d.ts +11 -0
  87. package/dist/editor/page-editor-chrome/FindReplaceHighlightings.js +117 -0
  88. package/dist/editor/page-editor-chrome/FindReplaceHighlightings.js.map +1 -0
  89. package/dist/editor/page-editor-chrome/PageEditorChrome.js +2 -1
  90. package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
  91. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +8 -1
  92. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -1
  93. package/dist/editor/page-viewer/MiniMap.d.ts +6 -2
  94. package/dist/editor/page-viewer/MiniMap.js +139 -21
  95. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  96. package/dist/editor/page-viewer/PageViewerFrame.js +21 -5
  97. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  98. package/dist/editor/page-viewer/bridgeKeyboardShortcuts.d.ts +6 -0
  99. package/dist/editor/page-viewer/bridgeKeyboardShortcuts.js +19 -1
  100. package/dist/editor/page-viewer/bridgeKeyboardShortcuts.js.map +1 -1
  101. package/dist/editor/page-viewer/minimapViewport.d.ts +2 -0
  102. package/dist/editor/page-viewer/minimapViewport.js +31 -0
  103. package/dist/editor/page-viewer/minimapViewport.js.map +1 -0
  104. package/dist/editor/reviews/CommentEditor.js +34 -29
  105. package/dist/editor/reviews/CommentEditor.js.map +1 -1
  106. package/dist/editor/reviews/CommentImageAttachments.d.ts +12 -0
  107. package/dist/editor/reviews/CommentImageAttachments.js +19 -0
  108. package/dist/editor/reviews/CommentImageAttachments.js.map +1 -0
  109. package/dist/editor/reviews/SuggestionDisplayPopover.d.ts +3 -1
  110. package/dist/editor/reviews/SuggestionDisplayPopover.js +7 -2
  111. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  112. package/dist/editor/reviews/commentAgentAttachments.d.ts +5 -0
  113. package/dist/editor/reviews/commentAgentAttachments.js +51 -0
  114. package/dist/editor/reviews/commentAgentAttachments.js.map +1 -0
  115. package/dist/editor/reviews/commentAi.d.ts +4 -2
  116. package/dist/editor/reviews/commentAi.js +19 -12
  117. package/dist/editor/reviews/commentAi.js.map +1 -1
  118. package/dist/editor/reviews/useCommentImagePaste.d.ts +41 -14
  119. package/dist/editor/reviews/useCommentImagePaste.js +102 -63
  120. package/dist/editor/reviews/useCommentImagePaste.js.map +1 -1
  121. package/dist/editor/services/agentStatus.d.ts +1 -0
  122. package/dist/editor/services/agentStatus.js +14 -0
  123. package/dist/editor/services/agentStatus.js.map +1 -1
  124. package/dist/editor/settings/index/RecentBatchesList.js +2 -1
  125. package/dist/editor/settings/index/RecentBatchesList.js.map +1 -1
  126. package/dist/editor/sidebar/ComponentTree.js +6 -3
  127. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  128. package/dist/editor/sidebar/OperationItem.js +2 -1
  129. package/dist/editor/sidebar/OperationItem.js.map +1 -1
  130. package/dist/editor/sidebar/liveComponentThumbnails.d.ts +6 -2
  131. package/dist/editor/sidebar/liveComponentThumbnails.js +7 -2
  132. package/dist/editor/sidebar/liveComponentThumbnails.js.map +1 -1
  133. package/dist/editor/ui/ParheliaMark.d.ts +15 -0
  134. package/dist/editor/ui/ParheliaMark.js +12 -0
  135. package/dist/editor/ui/ParheliaMark.js.map +1 -0
  136. package/dist/editor/undoEligibility.d.ts +2 -0
  137. package/dist/editor/undoEligibility.js +8 -0
  138. package/dist/editor/undoEligibility.js.map +1 -0
  139. package/dist/editor/utils/keyboardNavigation.js +16 -0
  140. package/dist/editor/utils/keyboardNavigation.js.map +1 -1
  141. package/dist/index.d.ts +1 -0
  142. package/dist/index.js +1 -0
  143. package/dist/index.js.map +1 -1
  144. package/dist/lib/utils.d.ts +5 -0
  145. package/dist/lib/utils.js +7 -0
  146. package/dist/lib/utils.js.map +1 -1
  147. package/dist/revision.d.ts +2 -2
  148. package/dist/revision.js +2 -2
  149. package/dist/splash-screen/ModernSplashScreen.js +4 -3
  150. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  151. package/dist/splash-screen/RecentPages.js +3 -3
  152. package/dist/splash-screen/RecentPages.js.map +1 -1
  153. package/dist/task-board/TaskBoardWorkspace.js +179 -33
  154. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  155. package/dist/task-board/components/AttentionPromptDialog.d.ts +13 -0
  156. package/dist/task-board/components/AttentionPromptDialog.js +45 -0
  157. package/dist/task-board/components/AttentionPromptDialog.js.map +1 -0
  158. package/dist/task-board/components/GoalDataView.d.ts +22 -1
  159. package/dist/task-board/components/GoalDataView.js +63 -17
  160. package/dist/task-board/components/GoalDataView.js.map +1 -1
  161. package/dist/task-board/components/GoalFindingsTable.d.ts +43 -0
  162. package/dist/task-board/components/GoalFindingsTable.js +160 -0
  163. package/dist/task-board/components/GoalFindingsTable.js.map +1 -0
  164. package/dist/task-board/components/GoalsMatrix.js +33 -6
  165. package/dist/task-board/components/GoalsMatrix.js.map +1 -1
  166. package/dist/task-board/components/IssuePanel.d.ts +31 -0
  167. package/dist/task-board/components/IssuePanel.js +268 -0
  168. package/dist/task-board/components/IssuePanel.js.map +1 -0
  169. package/dist/task-board/components/ProjectGoalsPanel.js +66 -13
  170. package/dist/task-board/components/ProjectGoalsPanel.js.map +1 -1
  171. package/dist/task-board/components/SubgoalFindings.d.ts +40 -0
  172. package/dist/task-board/components/SubgoalFindings.js +88 -0
  173. package/dist/task-board/components/SubgoalFindings.js.map +1 -0
  174. package/dist/task-board/components/TaskAgentPanel.d.ts +2 -0
  175. package/dist/task-board/components/TaskAgentPanel.js +5 -4
  176. package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
  177. package/dist/task-board/components/TaskDetailPanel.js +8 -12
  178. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  179. package/dist/task-board/components/WizardCommunicationCenter.d.ts +2 -0
  180. package/dist/task-board/components/WizardCommunicationCenter.js +5 -3
  181. package/dist/task-board/components/WizardCommunicationCenter.js.map +1 -1
  182. package/dist/task-board/components/WizardCommunicationShared.js +2 -1
  183. package/dist/task-board/components/WizardCommunicationShared.js.map +1 -1
  184. package/dist/task-board/components/WizardTask.d.ts +2 -0
  185. package/dist/task-board/components/WizardTask.js +2 -2
  186. package/dist/task-board/components/WizardTask.js.map +1 -1
  187. package/dist/task-board/components/WizardTaskDetailsPanel.d.ts +4 -0
  188. package/dist/task-board/components/WizardTaskDetailsPanel.js +16 -3
  189. package/dist/task-board/components/WizardTaskDetailsPanel.js.map +1 -1
  190. package/dist/task-board/services/issueService.d.ts +193 -0
  191. package/dist/task-board/services/issueService.js +97 -0
  192. package/dist/task-board/services/issueService.js.map +1 -0
  193. package/dist/task-board/types.d.ts +24 -0
  194. package/dist/task-board/utils/projectDashboardModel.js +8 -0
  195. package/dist/task-board/utils/projectDashboardModel.js.map +1 -1
  196. package/package.json +1 -1
  197. package/styles.css +57 -3
@@ -34,6 +34,8 @@ import { normalizeTaskStatus } from "./taskStatus";
34
34
  import { getTaskExecutionDisplay, } from "./taskExecutionStatus";
35
35
  import { indexTaskExecutionRecords, overlayTaskExecutionList, taskExecutionRecordsEqual, } from "./taskExecutionRecords";
36
36
  import { WizardCommunicationCenter } from "./components/WizardCommunicationCenter";
37
+ import { AttentionPromptDialog } from "./components/AttentionPromptDialog";
38
+ import { listAttentionTasks } from "./utils/projectDashboardModel";
37
39
  import { Loader2 } from "lucide-react";
38
40
  import { MainContentTree } from "../editor/sidebar/MainContentTree";
39
41
  import { useTaskBoardAgentPanelState } from "./useTaskBoardAgentPanelState";
@@ -54,6 +56,12 @@ const TASKBOARD_PROJECT_QUERY_KEY = "tbProjectId";
54
56
  const TASKBOARD_TASK_QUERY_KEY = "tbTaskId";
55
57
  const TASKBOARD_VIEW_QUERY_KEY = "tbView";
56
58
  const WS_REFRESH_DELAY_MS = 500;
59
+ const ATTENTION_PROMPT_DELAY_MS = 250;
60
+ // Pause after the user handles an action before the panel moves on to the
61
+ // next task that needs them, so the confirmation (approved, answered) is seen.
62
+ const WIZARD_AUTO_ADVANCE_DELAY_MS = 2000;
63
+ /** Retry delays when goal issue counts come back flagged as inconsistent. */
64
+ const GOAL_CONSISTENCY_BACKOFF_MS = [500, 1000, 2000];
57
65
  const TASKBOARD_VIEW_TAB_IDS = [
58
66
  "overview",
59
67
  "kanban",
@@ -283,7 +291,15 @@ export function TaskBoardWorkspace() {
283
291
  const directSubprojectIdsRef = useRef(new Set());
284
292
  const tasksRef = useRef([]);
285
293
  const previousTaskStatusesRef = useRef({});
286
- const previousWizardAttentionSignatureRef = useRef("");
294
+ const previousAttentionSnapshotRef = useRef(null);
295
+ const wizardDisplayedTaskIdRef = useRef(null);
296
+ const selectedTaskSourceRef = useRef(null);
297
+ const wizardDisplayedNeedsAttentionRef = useRef(false);
298
+ const previousDisplayedAttentionRef = useRef(null);
299
+ const autoAdvanceTimeoutRef = useRef(null);
300
+ const findNextAttentionTaskIdRef = useRef(() => null);
301
+ const [autoAdvancePending, setAutoAdvancePending] = useState(false);
302
+ const [attentionPromptTaskIds, setAttentionPromptTaskIds] = useState([]);
287
303
  const autoSelectedProjectIdsRef = useRef(new Set());
288
304
  const syncTaskBoardStateFromUrl = useCallback(() => {
289
305
  const nextState = getTaskBoardStateFromUrl();
@@ -682,38 +698,80 @@ export function TaskBoardWorkspace() {
682
698
  return;
683
699
  setWizardApprovedTaskId(null);
684
700
  }, [wizardApprovedTaskId, wizardDisplayedTask?.taskId]);
701
+ wizardDisplayedTaskIdRef.current = wizardDisplayedTask?.taskId ?? null;
702
+ selectedTaskSourceRef.current = selectedTaskSource;
703
+ const wizardDisplayedNeedsAttention = !!wizardDisplayedTask &&
704
+ (Boolean(wizardDisplayedExecutionDisplay?.needsAttention) ||
705
+ normalizeTaskStatus(wizardDisplayedTask.status, wizardDisplayedTask.executionState) === "Review");
706
+ wizardDisplayedNeedsAttentionRef.current = wizardDisplayedNeedsAttention;
707
+ // Dashboard attention prompt: open a semi-modal dialog when a task the user
708
+ // already saw newly needs them. Only transitions count, so opening the board
709
+ // with pending approvals, late-loading subproject lists, or re-renders never
710
+ // prompt. The check is deferred a tick so the panel's own auto-select can
711
+ // claim the task first; a task that is already displayed is not prompted.
685
712
  useEffect(() => {
686
- const attentionSnapshot = JSON.stringify({
687
- selectedTaskId,
688
- selectedTaskSource,
689
- wizardPinnedTaskId,
690
- wizardForceOverview,
691
- attentionTaskId: wizardAttentionState.task?.taskId ?? null,
692
- attentionAgentId: wizardAttentionState.agentId ?? null,
693
- summaryState: wizardAttentionState.summaryState,
694
- totalCount: wizardAttentionState.stats.totalCount,
695
- completedCount: wizardAttentionState.stats.completedCount,
696
- approvalCount: wizardAttentionState.stats.approvalCount,
697
- blockedCount: wizardAttentionState.stats.blockedCount,
698
- runningCount: wizardAttentionState.stats.runningCount,
699
- displayedTaskId: wizardDisplayedTask?.taskId ?? null,
700
- displayedExecutionLabel: wizardDisplayedExecutionDisplay?.label ?? null,
701
- displayedNeedsAttention: wizardDisplayedExecutionDisplay?.needsAttention ?? null,
702
- });
703
- if (previousWizardAttentionSignatureRef.current === attentionSnapshot) {
713
+ if (!isOverviewView || isTasksLoading || subprojectCountsLoading) {
714
+ previousAttentionSnapshotRef.current = null;
704
715
  return;
705
716
  }
706
- previousWizardAttentionSignatureRef.current = attentionSnapshot;
717
+ const attentionIds = new Set(listAttentionTasks(orderedWizardTasks).map(({ task }) => task.taskId));
718
+ const knownIds = new Set(orderedWizardTasks.map((task) => task.taskId));
719
+ const previous = previousAttentionSnapshotRef.current;
720
+ previousAttentionSnapshotRef.current = { knownIds, attentionIds };
721
+ if (!previous)
722
+ return;
723
+ const arrivedIds = [...attentionIds].filter((taskId) => previous.knownIds.has(taskId) && !previous.attentionIds.has(taskId));
724
+ if (arrivedIds.length === 0)
725
+ return;
726
+ const timeoutId = window.setTimeout(() => {
727
+ const displayedId = wizardDisplayedTaskIdRef.current;
728
+ const promptIds = arrivedIds.filter((taskId) => taskId !== displayedId);
729
+ if (promptIds.length === 0)
730
+ return;
731
+ // One action at a time: while the displayed task itself needs the user,
732
+ // new arrivals just queue up in "Waiting for you" and the panel moves on
733
+ // to them by itself once this one is handled.
734
+ if (wizardDisplayedNeedsAttentionRef.current)
735
+ return;
736
+ // Nothing the user chose to look at is in the way: show the action
737
+ // directly instead of asking first.
738
+ if (selectedTaskSourceRef.current !== "user") {
739
+ setWizardForceOverview(false);
740
+ setTaskSelection(promptIds[0], {
741
+ source: "system",
742
+ pinInWizard: true,
743
+ });
744
+ return;
745
+ }
746
+ setAttentionPromptTaskIds((current) => [
747
+ ...current,
748
+ ...promptIds.filter((taskId) => !current.includes(taskId)),
749
+ ]);
750
+ }, ATTENTION_PROMPT_DELAY_MS);
751
+ return () => window.clearTimeout(timeoutId);
707
752
  }, [
708
- selectedTaskId,
709
- selectedTaskSource,
710
- wizardAttentionState,
711
- wizardDisplayedExecutionDisplay?.label,
712
- wizardDisplayedExecutionDisplay?.needsAttention,
713
- wizardDisplayedTask?.taskId,
714
- wizardForceOverview,
715
- wizardPinnedTaskId,
753
+ isOverviewView,
754
+ isTasksLoading,
755
+ subprojectCountsLoading,
756
+ orderedWizardTasks,
757
+ setTaskSelection,
716
758
  ]);
759
+ const attentionPromptTasks = useMemo(() => {
760
+ if (attentionPromptTaskIds.length === 0)
761
+ return [];
762
+ const displayedId = wizardDisplayedTask?.taskId ?? null;
763
+ const attentionById = new Map(listAttentionTasks(orderedWizardTasks).map(({ task }) => [
764
+ task.taskId,
765
+ task,
766
+ ]));
767
+ return attentionPromptTaskIds
768
+ .filter((taskId) => taskId !== displayedId)
769
+ .map((taskId) => attentionById.get(taskId))
770
+ .filter((task) => Boolean(task));
771
+ }, [attentionPromptTaskIds, orderedWizardTasks, wizardDisplayedTask?.taskId]);
772
+ const dismissAttentionPrompt = useCallback(() => {
773
+ setAttentionPromptTaskIds([]);
774
+ }, []);
717
775
  const clearWizardCloseTransitionTimeout = useCallback(() => {
718
776
  if (wizardCloseTransitionTimeoutRef.current !== null) {
719
777
  window.clearTimeout(wizardCloseTransitionTimeoutRef.current);
@@ -748,6 +806,61 @@ export function TaskBoardWorkspace() {
748
806
  }
749
807
  return null;
750
808
  }, [orderedWizardTasks, wizardExecutionRecordsByTaskId]);
809
+ findNextAttentionTaskIdRef.current = findNextAttentionTaskId;
810
+ // One action at a time: once the displayed task stops needing the user (its
811
+ // tool call was approved, its question answered, it was reviewed), move on to
812
+ // the next task that does, after a short pause so the confirmation is seen.
813
+ const clearAutoAdvance = useCallback(() => {
814
+ if (autoAdvanceTimeoutRef.current !== null) {
815
+ window.clearTimeout(autoAdvanceTimeoutRef.current);
816
+ autoAdvanceTimeoutRef.current = null;
817
+ }
818
+ setAutoAdvancePending(false);
819
+ }, []);
820
+ useEffect(() => {
821
+ const current = wizardDisplayedTask
822
+ ? {
823
+ taskId: wizardDisplayedTask.taskId,
824
+ needsAttention: wizardDisplayedNeedsAttention,
825
+ }
826
+ : null;
827
+ const previous = previousDisplayedAttentionRef.current;
828
+ previousDisplayedAttentionRef.current = current;
829
+ if (!current || !previous || previous.taskId !== current.taskId) {
830
+ clearAutoAdvance();
831
+ return;
832
+ }
833
+ if (!isOverviewView)
834
+ return;
835
+ if (!previous.needsAttention || current.needsAttention)
836
+ return;
837
+ if (!findNextAttentionTaskId(current.taskId))
838
+ return;
839
+ clearAutoAdvance();
840
+ const sourceTaskId = current.taskId;
841
+ setAutoAdvancePending(true);
842
+ autoAdvanceTimeoutRef.current = window.setTimeout(() => {
843
+ autoAdvanceTimeoutRef.current = null;
844
+ setAutoAdvancePending(false);
845
+ if (wizardDisplayedTaskIdRef.current !== sourceTaskId)
846
+ return;
847
+ if (wizardDisplayedNeedsAttentionRef.current)
848
+ return;
849
+ const nextTaskId = findNextAttentionTaskIdRef.current(sourceTaskId);
850
+ if (!nextTaskId)
851
+ return;
852
+ setWizardForceOverview(false);
853
+ setTaskSelection(nextTaskId, { source: "system", pinInWizard: true });
854
+ }, WIZARD_AUTO_ADVANCE_DELAY_MS);
855
+ }, [
856
+ clearAutoAdvance,
857
+ findNextAttentionTaskId,
858
+ isOverviewView,
859
+ setTaskSelection,
860
+ wizardDisplayedNeedsAttention,
861
+ wizardDisplayedTask,
862
+ ]);
863
+ useEffect(() => clearAutoAdvance, [clearAutoAdvance]);
751
864
  // ── data fetching ──
752
865
  const refreshProjects = useCallback(async (options) => {
753
866
  projectsRequestCountRef.current += 1;
@@ -884,7 +997,7 @@ export function TaskBoardWorkspace() {
884
997
  const indexed = indexTaskExecutionRecords(records);
885
998
  setExecutionRecordsByTaskId(indexed);
886
999
  }, []);
887
- const refreshGoals = useCallback(async (projectId, includeDescendants) => {
1000
+ const refreshGoals = useCallback(async (projectId, includeDescendants, attempt = 0) => {
888
1001
  const generation = ++goalsRequestGenerationRef.current;
889
1002
  latestGoalsProjectIdRef.current = projectId;
890
1003
  setGoalsLoading(true);
@@ -905,12 +1018,24 @@ export function TaskBoardWorkspace() {
905
1018
  toast.error(message);
906
1019
  return;
907
1020
  }
908
- setGoals(result.data?.goals ?? []);
1021
+ const goals = result.data?.goals ?? [];
1022
+ setGoals(goals);
909
1023
  setGoalCatalog(result.data?.catalog ?? []);
910
1024
  setGoalPermissions(result.data?.permissions ?? {});
911
1025
  setGoalDisplaySpecs(result.data?.displaySpecs ?? {});
912
1026
  setGoalStateScope({ projectId, includeDescendants });
913
1027
  setGoalsError(null);
1028
+ // Issue counts read while issues were changing: read again with
1029
+ // bounded backoff (500 ms, 1 s, 2 s); the last result stays flagged
1030
+ // and the goal card says so. A later goal:changed is not relied on.
1031
+ const inconsistent = goals.some((goal) => goal.issueSummaryConsistent === false);
1032
+ if (inconsistent && attempt < GOAL_CONSISTENCY_BACKOFF_MS.length) {
1033
+ window.setTimeout(() => {
1034
+ if (latestGoalsProjectIdRef.current === projectId &&
1035
+ generation === goalsRequestGenerationRef.current)
1036
+ void refreshGoals(projectId, includeDescendants, attempt + 1);
1037
+ }, GOAL_CONSISTENCY_BACKOFF_MS[attempt]);
1038
+ }
914
1039
  }
915
1040
  finally {
916
1041
  if (latestGoalsProjectIdRef.current === projectId &&
@@ -929,6 +1054,10 @@ export function TaskBoardWorkspace() {
929
1054
  }
930
1055
  };
931
1056
  }, [refreshGoals, includeGoalSubprojects]);
1057
+ // Issue revisions and QA state may have changed while the socket was offline.
1058
+ useEffect(() => {
1059
+ refreshGoalsRef.current?.();
1060
+ }, [editContext?.socketConnectionVersion]);
932
1061
  // Load goals when the Goals tab or the dashboard is active (and on
933
1062
  // project/scope change); the dashboard shows each goal's figures.
934
1063
  const needsGoals = isGoalsView || isOverviewView;
@@ -1777,7 +1906,8 @@ export function TaskBoardWorkspace() {
1777
1906
  }, [refreshSubprojectTaskCounts]);
1778
1907
  useEffect(() => {
1779
1908
  previousTaskStatusesRef.current = {};
1780
- previousWizardAttentionSignatureRef.current = "";
1909
+ previousAttentionSnapshotRef.current = null;
1910
+ setAttentionPromptTaskIds([]);
1781
1911
  transitionGoalTaskNavigation({
1782
1912
  type: "projectSelected",
1783
1913
  projectId: selectedProjectId,
@@ -2074,6 +2204,9 @@ export function TaskBoardWorkspace() {
2074
2204
  const wizardNextReviewTaskId = useMemo(() => wizardDisplayedTask
2075
2205
  ? findNextAttentionTaskId(wizardDisplayedTask.taskId)
2076
2206
  : null, [findNextAttentionTaskId, wizardDisplayedTask]);
2207
+ const wizardNextReviewTask = useMemo(() => wizardNextReviewTaskId
2208
+ ? (orderedWizardTasks.find((task) => task.taskId === wizardNextReviewTaskId) ?? null)
2209
+ : null, [orderedWizardTasks, wizardNextReviewTaskId]);
2077
2210
  const handleReviewNextWizardTask = useCallback(() => {
2078
2211
  clearWizardCloseTransitionTimeout();
2079
2212
  setWizardApprovedTaskId(null);
@@ -2115,7 +2248,7 @@ export function TaskBoardWorkspace() {
2115
2248
  });
2116
2249
  }
2117
2250
  await refreshVisibleTaskData();
2118
- }, showApprovedState: wizardDisplayedTask?.taskId === wizardApprovedTaskId, hasNextReviewTask: Boolean(wizardNextReviewTaskId), onReviewNext: handleReviewNextWizardTask, onInteractionSubmitted: () => {
2251
+ }, showApprovedState: wizardDisplayedTask?.taskId === wizardApprovedTaskId, hasNextReviewTask: Boolean(wizardNextReviewTaskId), nextAttentionTask: wizardNextReviewTask, autoAdvancePending: autoAdvancePending, onReviewNext: handleReviewNextWizardTask, onInteractionSubmitted: () => {
2119
2252
  handleCloseWizardTask({ advanceToNext: true });
2120
2253
  }, onCloseTask: () => handleCloseWizardTask(), onSelectTask: (taskId) => {
2121
2254
  clearWizardCloseTransitionTimeout();
@@ -2141,6 +2274,8 @@ export function TaskBoardWorkspace() {
2141
2274
  setTaskSelection,
2142
2275
  wizardApprovedTaskId,
2143
2276
  wizardNextReviewTaskId,
2277
+ wizardNextReviewTask,
2278
+ autoAdvancePending,
2144
2279
  ]);
2145
2280
  const isProjectViewLoading = !!selectedProjectId && projectViewLoadingId === selectedProjectId;
2146
2281
  const goalScopeMatches = goalScopesMatch(goalStateScope, {
@@ -2253,7 +2388,12 @@ export function TaskBoardWorkspace() {
2253
2388
  return (_jsxs("div", { className: "flex h-full min-h-0 min-w-0 flex-col gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "No project selected", selectedProjectDescription: selectedProject?.project.description, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canCreateTask: canEditTasks, onCreateTask: () => setCreateDialogOpen(true), showCreateTaskButton: false }), sharedViewSwitcher, _jsx("div", { className: "mt-3 min-h-0 flex-1", children: _jsx(ProjectDashboardView, { projectId: selectedProjectId, projectTitle: selectedProject?.project.title ?? "No project selected", tasks: tasksWithDisplayAssignees, projectTasksLoading: isTasksLoading, dependencies: dependencies, subprojectTaskLists: wizardSubprojectTaskLists, subprojectTasksLoading: subprojectCountsLoading, selectedTaskId: wizardDisplayedTask?.taskId ?? null, onSelectTask: (id) => {
2254
2389
  setWizardForceOverview(false);
2255
2390
  setTaskSelection(id, { source: "user", pinInWizard: true });
2256
- }, projectCostUsed: selectedProjectCumulativeCostUsed, projectCostLimit: selectedProject?.project.costLimit ?? null, goals: goalScopeMatches ? goals : [], goalsLoading: goalsLoading || !goalScopeMatches, goalDisplaySpecs: goalScopeMatches ? goalDisplaySpecs : {}, onOpenGoals: () => setActiveTab(getTaskBoardViewTabIndex("goals")), communicationPanel: wizardDisplayedTask ? wizardCommunicationPanel : undefined }) })] }));
2391
+ }, projectCostUsed: selectedProjectCumulativeCostUsed, projectCostLimit: selectedProject?.project.costLimit ?? null, goals: goalScopeMatches ? goals : [], goalsLoading: goalsLoading || !goalScopeMatches, goalDisplaySpecs: goalScopeMatches ? goalDisplaySpecs : {}, onOpenGoals: () => setActiveTab(getTaskBoardViewTabIndex("goals")), communicationPanel: wizardDisplayedTask ? wizardCommunicationPanel : undefined }) }), _jsx(AttentionPromptDialog, { tasks: attentionPromptTasks, onReview: (taskId) => {
2392
+ setAttentionPromptTaskIds([]);
2393
+ clearWizardCloseTransitionTimeout();
2394
+ setWizardForceOverview(false);
2395
+ setTaskSelection(taskId, { source: "user", pinInWizard: true });
2396
+ }, onDismiss: dismissAttentionPrompt })] }));
2257
2397
  }
2258
2398
  if (isSettingsView) {
2259
2399
  return (_jsxs("div", { className: "flex h-full min-h-0 min-w-0 flex-col gap-3", children: [_jsx(ProjectDashboard, { projectId: selectedProjectId, selectedProjectTitle: selectedProject?.project.title ?? "No project selected", selectedProjectDescription: selectedProject?.project.description, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canCreateTask: canEditTasks, onCreateTask: () => setCreateDialogOpen(true), showCreateTaskButton: false }), sharedViewSwitcher, _jsx("div", { className: "mt-3", children: _jsx(ProjectSettingsPanel, { projectId: selectedProjectId, selectedProject: selectedProject, projects: projects, canOpenSourceTemplate: isAdministrator, onOpenSourceTemplate: handleOpenSourceTemplate, onDeleteProject: handleDeleteProject, onChanged: async () => {
@@ -2335,6 +2475,9 @@ export function TaskBoardWorkspace() {
2335
2475
  wizardSubprojectTaskLists,
2336
2476
  wizardDisplayedTask?.taskId,
2337
2477
  wizardCommunicationPanel,
2478
+ attentionPromptTasks,
2479
+ dismissAttentionPrompt,
2480
+ clearWizardCloseTransitionTimeout,
2338
2481
  setTaskSelection,
2339
2482
  refreshProjects,
2340
2483
  refreshGoals,
@@ -2412,6 +2555,8 @@ export function TaskBoardWorkspace() {
2412
2555
  ]);
2413
2556
  const agentTerminalPanel = useMemo(() => (_jsx(TaskAgentPanel, { agentId: displayAgentId, mode: agentPanelMode, label: selectedTaskIsPlan ? "Planner" : "Task Agent", taskDetailsContent: taskDetailsPopoverContent ?? undefined, taskDetailsPopoverOpen: isTaskDetailsPopoverOpen, onTaskDetailsPopoverOpenChange: setIsTaskDetailsPopoverOpen, assignedAgentStatus: undefined, onReopenTask: selectedTask && canEditTasks
2414
2557
  ? () => void handleReopenSelectedTask()
2558
+ : undefined, onClose: selectedTask
2559
+ ? () => setTaskSelection(null, { source: "user" })
2415
2560
  : undefined })), [
2416
2561
  displayAgentId,
2417
2562
  agentPanelMode,
@@ -2421,6 +2566,7 @@ export function TaskBoardWorkspace() {
2421
2566
  canEditTasks,
2422
2567
  selectedTask,
2423
2568
  handleReopenSelectedTask,
2569
+ setTaskSelection,
2424
2570
  ]);
2425
2571
  const buildRightHandPanels = useCallback((showAgentColumn) => {
2426
2572
  const renderDetailPanel = (collapsedFlag) => (_jsx(TaskDetailPanel, { project: selectedTaskProject, projects: projects, task: selectedTask, allTasks: taskSelectionUniverse, dependencies: dependencies, onTaskChanged: () => {