@parhelia/core 0.1.12468 → 0.1.12477

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 (134) hide show
  1. package/dist/config/config.js +2 -21
  2. package/dist/config/config.js.map +1 -1
  3. package/dist/config/types.d.ts +0 -2
  4. package/dist/config/types.js.map +1 -1
  5. package/dist/editor/ConfirmationDialog.js +4 -20
  6. package/dist/editor/ConfirmationDialog.js.map +1 -1
  7. package/dist/editor/ContentTree.js +7 -19
  8. package/dist/editor/ContentTree.js.map +1 -1
  9. package/dist/editor/Editor.js.map +1 -1
  10. package/dist/editor/PictureCropper.js +41 -45
  11. package/dist/editor/PictureCropper.js.map +1 -1
  12. package/dist/editor/ai/AgentTerminal.js +8 -97
  13. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  14. package/dist/editor/ai/AgentTerminalStatusBar.js +0 -65
  15. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  16. package/dist/editor/ai/Agents.js +0 -19
  17. package/dist/editor/ai/Agents.js.map +1 -1
  18. package/dist/editor/ai/AiResponseMessage.js +0 -5
  19. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  20. package/dist/editor/ai/ContentInspectorPopover.d.ts +0 -1
  21. package/dist/editor/ai/ContentInspectorPopover.js +8 -22
  22. package/dist/editor/ai/ContentInspectorPopover.js.map +1 -1
  23. package/dist/editor/ai/ToolCallDisplay.d.ts +0 -2
  24. package/dist/editor/ai/ToolCallDisplay.js +11 -54
  25. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  26. package/dist/editor/ai/dialogs/AgentDialogHandler.js +3 -32
  27. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  28. package/dist/editor/ai/dialogs/QuestionnaireInline.js +20 -55
  29. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  30. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +4 -7
  31. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  32. package/dist/editor/ai/types.d.ts +0 -2
  33. package/dist/editor/services/agentService.d.ts +0 -27
  34. package/dist/editor/services/agentService.js +2 -11
  35. package/dist/editor/services/agentService.js.map +1 -1
  36. package/dist/editor/services/aiService.js +3 -54
  37. package/dist/editor/services/aiService.js.map +1 -1
  38. package/dist/editor/services/serviceHelper.js +2 -5
  39. package/dist/editor/services/serviceHelper.js.map +1 -1
  40. package/dist/editor/settings/About.js +4 -40
  41. package/dist/editor/settings/About.js.map +1 -1
  42. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.d.ts +1 -7
  43. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js +3 -3
  44. package/dist/editor/settings/panels/ProjectTemplateAgentPanel.js.map +1 -1
  45. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +84 -165
  46. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  47. package/dist/editor/settings/panels/index.d.ts +0 -1
  48. package/dist/editor/settings/panels/index.js +0 -1
  49. package/dist/editor/settings/panels/index.js.map +1 -1
  50. package/dist/editor/sidebar/NavigationPanelItem.js +1 -1
  51. package/dist/editor/sidebar/NavigationPanelItem.js.map +1 -1
  52. package/dist/editor/sidebar/WorkspaceButton.js +1 -1
  53. package/dist/editor/sidebar/WorkspaceButton.js.map +1 -1
  54. package/dist/editor/tree-indicators/GutterColumns.js +4 -24
  55. package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
  56. package/dist/editor/tree-indicators/types.d.ts +0 -10
  57. package/dist/editor/ui/Splitter.d.ts +0 -1
  58. package/dist/editor/ui/Splitter.js +1 -7
  59. package/dist/editor/ui/Splitter.js.map +1 -1
  60. package/dist/licensing/LicenseContext.js +4 -40
  61. package/dist/licensing/LicenseContext.js.map +1 -1
  62. package/dist/licensing/LicenseOverlay.js +1 -1
  63. package/dist/licensing/LicenseOverlay.js.map +1 -1
  64. package/dist/licensing/types.d.ts +1 -3
  65. package/dist/licensing/types.js.map +1 -1
  66. package/dist/revision.d.ts +2 -2
  67. package/dist/revision.js +2 -2
  68. package/dist/task-board/TaskBoardWorkspace.js +354 -165
  69. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  70. package/dist/task-board/assigneeDisplay.js +3 -1
  71. package/dist/task-board/assigneeDisplay.js.map +1 -1
  72. package/dist/task-board/components/CreateProjectDialog.js +1 -2
  73. package/dist/task-board/components/CreateProjectDialog.js.map +1 -1
  74. package/dist/task-board/components/ProjectDashboard.js +1 -2
  75. package/dist/task-board/components/ProjectDashboard.js.map +1 -1
  76. package/dist/task-board/components/ProjectSettingsDialog.d.ts +0 -1
  77. package/dist/task-board/components/ProjectSettingsDialog.js +12 -146
  78. package/dist/task-board/components/ProjectSettingsDialog.js.map +1 -1
  79. package/dist/task-board/components/TaskAgentPanel.d.ts +0 -1
  80. package/dist/task-board/components/TaskAgentPanel.js +2 -2
  81. package/dist/task-board/components/TaskAgentPanel.js.map +1 -1
  82. package/dist/task-board/components/TaskAssigneePicker.js +2 -2
  83. package/dist/task-board/components/TaskAssigneePicker.js.map +1 -1
  84. package/dist/task-board/components/TaskBoardMyTasksSidebar.js +1 -1
  85. package/dist/task-board/components/TaskBoardMyTasksSidebar.js.map +1 -1
  86. package/dist/task-board/components/TaskDetailDialog.d.ts +0 -1
  87. package/dist/task-board/components/TaskDetailDialog.js +2 -2
  88. package/dist/task-board/components/TaskDetailDialog.js.map +1 -1
  89. package/dist/task-board/components/TaskDetailPanel.d.ts +0 -1
  90. package/dist/task-board/components/TaskDetailPanel.js +8 -23
  91. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  92. package/dist/task-board/components/TaskRow.js +2 -3
  93. package/dist/task-board/components/TaskRow.js.map +1 -1
  94. package/dist/task-board/components/WizardTaskDetailsPanel.js +1 -2
  95. package/dist/task-board/components/WizardTaskDetailsPanel.js.map +1 -1
  96. package/dist/task-board/services/taskService.d.ts +1 -17
  97. package/dist/task-board/services/taskService.js +0 -56
  98. package/dist/task-board/services/taskService.js.map +1 -1
  99. package/dist/task-board/taskExecutionRecords.js +0 -2
  100. package/dist/task-board/taskExecutionRecords.js.map +1 -1
  101. package/dist/task-board/types.d.ts +1 -78
  102. package/dist/task-board/utils/taskDependencyOrdering.d.ts +0 -6
  103. package/dist/task-board/utils/taskDependencyOrdering.js +1 -138
  104. package/dist/task-board/utils/taskDependencyOrdering.js.map +1 -1
  105. package/dist/task-board/views/DependencyGraphView.d.ts +2 -5
  106. package/dist/task-board/views/DependencyGraphView.js +69 -261
  107. package/dist/task-board/views/DependencyGraphView.js.map +1 -1
  108. package/dist/task-board/views/KanbanView.js +1 -8
  109. package/dist/task-board/views/KanbanView.js.map +1 -1
  110. package/dist/task-board/views/ListView.js +1 -1
  111. package/dist/task-board/views/ListView.js.map +1 -1
  112. package/dist/task-board/views/WizardView.js +24 -30
  113. package/dist/task-board/views/WizardView.js.map +1 -1
  114. package/dist/tour/Tour.js +47 -0
  115. package/dist/tour/Tour.js.map +1 -1
  116. package/package.json +1 -1
  117. package/dist/editor/settings/panels/PersistentLogsPanel.d.ts +0 -2
  118. package/dist/editor/settings/panels/PersistentLogsPanel.js +0 -244
  119. package/dist/editor/settings/panels/PersistentLogsPanel.js.map +0 -1
  120. package/dist/task-board/components/ProjectExecutionUserPicker.d.ts +0 -14
  121. package/dist/task-board/components/ProjectExecutionUserPicker.js +0 -65
  122. package/dist/task-board/components/ProjectExecutionUserPicker.js.map +0 -1
  123. package/dist/task-board/components/TaskboardPersistentLogPanel.d.ts +0 -11
  124. package/dist/task-board/components/TaskboardPersistentLogPanel.js +0 -141
  125. package/dist/task-board/components/TaskboardPersistentLogPanel.js.map +0 -1
  126. package/dist/task-board/persistentLogCopy.d.ts +0 -7
  127. package/dist/task-board/persistentLogCopy.js +0 -80
  128. package/dist/task-board/persistentLogCopy.js.map +0 -1
  129. package/dist/task-board/persistentLogLabels.d.ts +0 -38
  130. package/dist/task-board/persistentLogLabels.js +0 -189
  131. package/dist/task-board/persistentLogLabels.js.map +0 -1
  132. package/dist/task-board/useTaskBoardAgentPanelState.d.ts +0 -34
  133. package/dist/task-board/useTaskBoardAgentPanelState.js +0 -283
  134. package/dist/task-board/useTaskBoardAgentPanelState.js.map +0 -1
@@ -4,6 +4,7 @@ import { toast } from "sonner";
4
4
  import { cn } from "../lib/utils";
5
5
  import { Splitter } from "../editor/ui/Splitter";
6
6
  import { deleteProject, getExecutionState, getGraphLayout, getProjects, getTasks, getDependencies, runOrchestrator, triggerPlanning, updateProject, updateTask, } from "./services/taskService";
7
+ import { getAgent, } from "../editor/services/agentService";
7
8
  import { loadAiProfiles } from "../editor/services/aiService";
8
9
  import { useEditContext } from "../editor/client/editContext";
9
10
  import { KanbanView } from "./views/KanbanView";
@@ -12,7 +13,7 @@ import { DependencyGraphView } from "./views/DependencyGraphView";
12
13
  import { WizardView } from "./views/WizardView";
13
14
  import { SimpleTabs } from "../editor/ui/SimpleTabs";
14
15
  import { TaskDetailPanel } from "./components/TaskDetailPanel";
15
- import { TaskAgentPanel } from "./components/TaskAgentPanel";
16
+ import { TaskAgentPanel, } from "./components/TaskAgentPanel";
16
17
  import { ProjectDashboard, } from "./components/ProjectDashboard";
17
18
  import { CreateTaskDialog } from "./components/CreateTaskDialog";
18
19
  import { AssignAgentDialog } from "./components/AssignAgentDialog";
@@ -35,7 +36,6 @@ import { WizardCommunicationCenter } from "./components/WizardCommunicationCente
35
36
  import { Button } from "../components/ui/button";
36
37
  import { Loader2, Plus } from "lucide-react";
37
38
  import { MainContentTree } from "../editor/sidebar/MainContentTree";
38
- import { useTaskBoardAgentPanelState } from "./useTaskBoardAgentPanelState";
39
39
  function TaskboardPreviewEditor({ itemDescriptor, }) {
40
40
  const slotContext = useEditorSlotContext({
41
41
  slotId: "taskboard-preview-slot",
@@ -50,7 +50,6 @@ const TASKBOARD_PROJECT_QUERY_KEY = "tbProjectId";
50
50
  const TASKBOARD_TASK_QUERY_KEY = "tbTaskId";
51
51
  const TASKBOARD_VIEW_QUERY_KEY = "tbView";
52
52
  const TASKBOARD_WIZARD_QUERY_KEY = "tbWizard";
53
- const WS_REFRESH_DELAY_MS = 500;
54
53
  function getInitialQueryValue(queryKey) {
55
54
  if (typeof window === "undefined")
56
55
  return null;
@@ -162,6 +161,11 @@ export function TaskBoardWorkspace() {
162
161
  const [subprojectTasksByProjectId, setSubprojectTasksByProjectId] = useState({});
163
162
  const [subprojectExecutionRecordsByProjectId, setSubprojectExecutionRecordsByProjectId,] = useState({});
164
163
  const [subprojectCountsLoading, setSubprojectCountsLoading] = useState(false);
164
+ const [previewItemName, setPreviewItemName] = useState("");
165
+ const [previewItemPath, setPreviewItemPath] = useState("");
166
+ const [agentContextItems, setAgentContextItems] = useState([]);
167
+ const [contextItemNamesByKey, setContextItemNamesByKey] = useState({});
168
+ const [contextItemPathsByKey, setContextItemPathsByKey] = useState({});
165
169
  const [wizardPinnedTaskId, setWizardPinnedTaskId] = useState(null);
166
170
  const [, setWizardPinnedTaskSource] = useState(null);
167
171
  const [wizardForceOverview, setWizardForceOverview] = useState(false);
@@ -179,15 +183,6 @@ export function TaskBoardWorkspace() {
179
183
  }, [setWizardPinnedTask]);
180
184
  const wsRefreshTimeoutRef = useRef(null);
181
185
  const wsSubprojectRefreshTimeoutRef = useRef(null);
182
- const wsRefreshQueuedRef = useRef(false);
183
- const wsRefreshRunningRef = useRef(false);
184
- const wsRefreshNeedsTasksRef = useRef(false);
185
- const wsRefreshNeedsExecutionStateRef = useRef(false);
186
- const wsRefreshNeedsDependenciesRef = useRef(false);
187
- const wsRefreshNeedsProjectsRef = useRef(false);
188
- const wsSubprojectRefreshQueuedRef = useRef(false);
189
- const wsSubprojectRefreshRunningRef = useRef(false);
190
- const wsSubprojectRefreshNeedsProjectsRef = useRef(false);
191
186
  const wizardCloseTransitionTimeoutRef = useRef(null);
192
187
  const projectsRequestCountRef = useRef(0);
193
188
  const tasksRequestCountRef = useRef(0);
@@ -416,16 +411,110 @@ export function TaskBoardWorkspace() {
416
411
  return selectedProject;
417
412
  return (projects.find((project) => project.project.projectId === selectedTask.projectId) ?? selectedProject);
418
413
  }, [projects, selectedProject, selectedTask]);
419
- const { agentPanelMode, canRunAssignedAgent, contextItemOptions, displayAgentId, handleSelectContextItem, hasMultipleContextItems, previewItemName, previewItemPath, previewItemVersion, runAssignedAgentDisabledReason, selectedContextItemValue, selectedTaskHasAssignedAgentProfile, selectedTaskIsBlocked, selectedTaskIsPlan, shouldHideAgentPanelForSelectedTask, } = useTaskBoardAgentPanelState({
420
- currentItemDescriptor,
421
- editContext,
422
- isMobile,
423
- mobileActiveTab,
414
+ const selectedTaskIsPlan = useMemo(() => {
415
+ const taskType = String(selectedTask?.taskType ?? "").toLowerCase();
416
+ return taskType === "plan" || selectedTask?.title === "Project Plan";
417
+ }, [selectedTask]);
418
+ const selectedTaskIsBlocked = useMemo(() => {
419
+ if (!selectedTask)
420
+ return false;
421
+ if (selectedTask.projectId !== selectedProjectId)
422
+ return false;
423
+ const blockerIds = dependencies
424
+ .filter((dependency) => dependency.taskId === selectedTask.taskId &&
425
+ dependency.dependencyType === "BlockedBy")
426
+ .map((dependency) => dependency.dependsOnTaskId);
427
+ if (blockerIds.length === 0)
428
+ return false;
429
+ const taskById = new Map(tasksWithDisplayAssignees.map((task) => [task.taskId, task]));
430
+ return blockerIds.some((blockerId) => {
431
+ const blockerTask = taskById.get(blockerId);
432
+ return (!blockerTask ||
433
+ normalizeTaskStatus(blockerTask.status, blockerTask.executionState) !==
434
+ "Done");
435
+ });
436
+ }, [
424
437
  selectedTask,
425
- selectedTaskId,
426
- setMobileActiveTab,
427
- showEditorPanel,
428
- });
438
+ selectedProjectId,
439
+ dependencies,
440
+ tasksWithDisplayAssignees,
441
+ ]);
442
+ const selectedTaskHasAssignedAgentProfile = useMemo(() => {
443
+ if (!selectedTask)
444
+ return false;
445
+ return selectedTask.assigneeType === "Agent" && !!selectedTask.assigneeId;
446
+ }, [selectedTask]);
447
+ const canRunAssignedAgent = useMemo(() => {
448
+ return !selectedTaskIsBlocked;
449
+ }, [selectedTaskIsBlocked]);
450
+ const runAssignedAgentDisabledReason = useMemo(() => {
451
+ if (selectedTaskIsBlocked) {
452
+ return "This task is blocked by unfinished dependencies.";
453
+ }
454
+ return undefined;
455
+ }, [selectedTaskIsBlocked]);
456
+ // ── current agent ID and panel mode for the right panel ──
457
+ const currentAgentId = useMemo(() => {
458
+ return getLinkedAgentId(selectedTask);
459
+ }, [selectedTask]);
460
+ const selectedTaskStatus = useMemo(() => {
461
+ if (!selectedTask)
462
+ return null;
463
+ return normalizeTaskStatus(selectedTask.status, selectedTask.executionState);
464
+ }, [selectedTask]);
465
+ const selectedTaskHasResultData = useMemo(() => {
466
+ return !!selectedTask?.resultData?.trim();
467
+ }, [selectedTask?.resultData]);
468
+ const shouldHideAgentPanelForSelectedTask = useMemo(() => {
469
+ return (!!selectedTask &&
470
+ selectedTaskIsPlan &&
471
+ selectedTaskStatus === "Done" &&
472
+ selectedTaskHasResultData);
473
+ }, [
474
+ selectedTask,
475
+ selectedTaskIsPlan,
476
+ selectedTaskStatus,
477
+ selectedTaskHasResultData,
478
+ ]);
479
+ const displayAgentId = useMemo(() => {
480
+ return currentAgentId;
481
+ }, [currentAgentId]);
482
+ useEffect(() => {
483
+ if (isMobile && selectedTaskId) {
484
+ setMobileActiveTab(1); // Task tab
485
+ }
486
+ }, [selectedTaskId, isMobile]);
487
+ useEffect(() => {
488
+ if (isMobile && displayAgentId && !shouldHideAgentPanelForSelectedTask) {
489
+ setMobileActiveTab(2); // Agent tab
490
+ }
491
+ }, [displayAgentId, isMobile, shouldHideAgentPanelForSelectedTask]);
492
+ useEffect(() => {
493
+ if (!isMobile)
494
+ return;
495
+ if (!shouldHideAgentPanelForSelectedTask)
496
+ return;
497
+ if (mobileActiveTab !== 2)
498
+ return;
499
+ setMobileActiveTab(1);
500
+ }, [isMobile, mobileActiveTab, shouldHideAgentPanelForSelectedTask]);
501
+ useEffect(() => {
502
+ if (!showEditorPanel && mobileActiveTab === 3) {
503
+ setMobileActiveTab(0);
504
+ }
505
+ }, [showEditorPanel, mobileActiveTab]);
506
+ const currentItemKey = useMemo(() => {
507
+ if (!currentItemDescriptor)
508
+ return "";
509
+ return `${currentItemDescriptor.id}|${currentItemDescriptor.language}|${currentItemDescriptor.version}`;
510
+ }, [currentItemDescriptor]);
511
+ const agentPanelMode = useMemo(() => {
512
+ if (!selectedTask)
513
+ return "no-task-selected";
514
+ if (displayAgentId)
515
+ return "agent";
516
+ return "no-agent";
517
+ }, [selectedTask, displayAgentId]);
429
518
  const wizardAttentionState = useMemo(() => {
430
519
  const openTasks = orderedWizardTasks.filter((task) => normalizeTaskStatus(task.status, task.executionState) !== "Done");
431
520
  let runningCount = 0;
@@ -523,6 +612,19 @@ export function TaskBoardWorkspace() {
523
612
  wizardForceOverview,
524
613
  wizardPinnedTaskId,
525
614
  ]);
615
+ useEffect(() => {
616
+ if (!wizardDisplayedTask ||
617
+ (wizardDisplayedExecutionDisplay?.label !== "Questions" &&
618
+ wizardDisplayedTask.executionState !== "WaitingForInput" &&
619
+ wizardDisplayedTask.executionState !== "WaitingForUser")) {
620
+ return;
621
+ }
622
+ }, [
623
+ wizardDisplayedTask,
624
+ wizardDisplayedAgentId,
625
+ wizardDisplayedExecutionDisplay?.label,
626
+ selectedProjectId,
627
+ ]);
526
628
  const clearWizardCloseTransitionTimeout = useCallback(() => {
527
629
  if (wizardCloseTransitionTimeoutRef.current !== null) {
528
630
  window.clearTimeout(wizardCloseTransitionTimeoutRef.current);
@@ -646,8 +748,7 @@ export function TaskBoardWorkspace() {
646
748
  toast.error(result.summary || "Failed to load tasks");
647
749
  return;
648
750
  }
649
- const nextTasks = result.data || [];
650
- setTasks(nextTasks);
751
+ setTasks(result.data || []);
651
752
  }
652
753
  finally {
653
754
  tasksRequestCountRef.current = Math.max(0, tasksRequestCountRef.current - 1);
@@ -932,114 +1033,6 @@ export function TaskBoardWorkspace() {
932
1033
  directSubprojects,
933
1034
  taskSelectionUniverse,
934
1035
  ]);
935
- const queueSelectedProjectRefresh = useCallback((options) => {
936
- if (options?.refreshTasks) {
937
- wsRefreshNeedsTasksRef.current = true;
938
- }
939
- if (options?.refreshExecutionState) {
940
- wsRefreshNeedsExecutionStateRef.current = true;
941
- }
942
- if (options?.refreshDependencies) {
943
- wsRefreshNeedsDependenciesRef.current = true;
944
- }
945
- if (options?.refreshProjects) {
946
- wsRefreshNeedsProjectsRef.current = true;
947
- }
948
- wsRefreshQueuedRef.current = true;
949
- if (wsRefreshTimeoutRef.current !== null || wsRefreshRunningRef.current) {
950
- return;
951
- }
952
- wsRefreshTimeoutRef.current = window.setTimeout(() => {
953
- wsRefreshTimeoutRef.current = null;
954
- const projectId = selectedProjectIdRef.current;
955
- const shouldRefreshTasks = wsRefreshNeedsTasksRef.current;
956
- const shouldRefreshExecutionState = wsRefreshNeedsExecutionStateRef.current;
957
- const shouldRefreshDependencies = wsRefreshNeedsDependenciesRef.current;
958
- const shouldRefreshProjects = wsRefreshNeedsProjectsRef.current;
959
- wsRefreshQueuedRef.current = false;
960
- wsRefreshNeedsTasksRef.current = false;
961
- wsRefreshNeedsExecutionStateRef.current = false;
962
- wsRefreshNeedsDependenciesRef.current = false;
963
- wsRefreshNeedsProjectsRef.current = false;
964
- const work = [];
965
- if (shouldRefreshProjects) {
966
- const refreshProjectsFn = refreshProjectsRef.current;
967
- if (refreshProjectsFn) {
968
- work.push(refreshProjectsFn());
969
- }
970
- }
971
- if (projectId) {
972
- if (shouldRefreshTasks) {
973
- const refreshTasksFn = refreshTasksRef.current;
974
- if (refreshTasksFn) {
975
- work.push(refreshTasksFn(projectId));
976
- }
977
- }
978
- if (shouldRefreshExecutionState) {
979
- const refreshExecutionStateFn = refreshExecutionStateRef.current;
980
- if (refreshExecutionStateFn) {
981
- work.push(refreshExecutionStateFn(projectId));
982
- }
983
- }
984
- if (shouldRefreshDependencies) {
985
- const refreshDependenciesFn = refreshDependenciesRef.current;
986
- if (refreshDependenciesFn) {
987
- work.push(refreshDependenciesFn(projectId));
988
- }
989
- }
990
- }
991
- if (work.length === 0) {
992
- return;
993
- }
994
- wsRefreshRunningRef.current = true;
995
- void Promise.all(work).finally(() => {
996
- wsRefreshRunningRef.current = false;
997
- if (wsRefreshQueuedRef.current ||
998
- wsRefreshNeedsTasksRef.current ||
999
- wsRefreshNeedsExecutionStateRef.current ||
1000
- wsRefreshNeedsDependenciesRef.current ||
1001
- wsRefreshNeedsProjectsRef.current) {
1002
- queueSelectedProjectRefresh();
1003
- }
1004
- });
1005
- }, WS_REFRESH_DELAY_MS);
1006
- }, []);
1007
- const queueSubprojectRefresh = useCallback((options) => {
1008
- if (options?.refreshProjects) {
1009
- wsSubprojectRefreshNeedsProjectsRef.current = true;
1010
- }
1011
- wsSubprojectRefreshQueuedRef.current = true;
1012
- if (wsSubprojectRefreshTimeoutRef.current !== null ||
1013
- wsSubprojectRefreshRunningRef.current) {
1014
- return;
1015
- }
1016
- wsSubprojectRefreshTimeoutRef.current = window.setTimeout(() => {
1017
- wsSubprojectRefreshTimeoutRef.current = null;
1018
- const shouldRefreshProjects = wsSubprojectRefreshNeedsProjectsRef.current;
1019
- wsSubprojectRefreshQueuedRef.current = false;
1020
- wsSubprojectRefreshNeedsProjectsRef.current = false;
1021
- const refreshSubprojectsFn = refreshSubprojectTaskCountsRef.current;
1022
- const refreshProjectsFn = refreshProjectsRef.current;
1023
- const work = [];
1024
- if (refreshSubprojectsFn) {
1025
- work.push(refreshSubprojectsFn());
1026
- }
1027
- if (shouldRefreshProjects && refreshProjectsFn) {
1028
- work.push(refreshProjectsFn());
1029
- }
1030
- if (work.length === 0) {
1031
- return;
1032
- }
1033
- wsSubprojectRefreshRunningRef.current = true;
1034
- void Promise.all(work).finally(() => {
1035
- wsSubprojectRefreshRunningRef.current = false;
1036
- if (wsSubprojectRefreshQueuedRef.current ||
1037
- wsSubprojectRefreshNeedsProjectsRef.current) {
1038
- queueSubprojectRefresh();
1039
- }
1040
- });
1041
- }, WS_REFRESH_DELAY_MS);
1042
- }, []);
1043
1036
  const handleRunOrchestrator = useCallback(async (options) => {
1044
1037
  if (!selectedProjectId)
1045
1038
  return;
@@ -1446,16 +1439,34 @@ export function TaskBoardWorkspace() {
1446
1439
  const payload = message?.payload;
1447
1440
  const projectId = payload?.projectId ?? payload?.ProjectId;
1448
1441
  const currentProjectId = selectedProjectIdRef.current;
1442
+ const refreshProjectsFn = refreshProjectsRef.current;
1449
1443
  if (!projectId ||
1450
1444
  !currentProjectId ||
1445
+ !refreshProjectsFn ||
1451
1446
  !isProjectWithinSelectedTree(projectId, currentProjectId)) {
1452
1447
  return;
1453
1448
  }
1454
- if (projectId === currentProjectId) {
1455
- queueSelectedProjectRefresh({ refreshProjects: true });
1456
- return;
1449
+ if (wsRefreshTimeoutRef.current !== null) {
1450
+ window.clearTimeout(wsRefreshTimeoutRef.current);
1451
+ wsRefreshTimeoutRef.current = null;
1457
1452
  }
1458
- queueSubprojectRefresh({ refreshProjects: true });
1453
+ wsRefreshTimeoutRef.current = window.setTimeout(() => {
1454
+ wsRefreshTimeoutRef.current = null;
1455
+ const selectedId = selectedProjectIdRef.current;
1456
+ const refreshProjectsFn = refreshProjectsRef.current;
1457
+ if (!selectedId || !refreshProjectsFn)
1458
+ return;
1459
+ void refreshProjectsFn();
1460
+ if (projectId === selectedId) {
1461
+ const refreshExecutionStateFn = refreshExecutionStateRef.current;
1462
+ if (refreshExecutionStateFn)
1463
+ void refreshExecutionStateFn(selectedId);
1464
+ return;
1465
+ }
1466
+ const refreshSubprojectsFn = refreshSubprojectTaskCountsRef.current;
1467
+ if (refreshSubprojectsFn)
1468
+ void refreshSubprojectsFn();
1469
+ }, 250);
1459
1470
  return;
1460
1471
  }
1461
1472
  if (messageType === "task:state-changed") {
@@ -1505,7 +1516,16 @@ export function TaskBoardWorkspace() {
1505
1516
  },
1506
1517
  };
1507
1518
  });
1508
- queueSubprojectRefresh();
1519
+ if (wsSubprojectRefreshTimeoutRef.current !== null) {
1520
+ window.clearTimeout(wsSubprojectRefreshTimeoutRef.current);
1521
+ wsSubprojectRefreshTimeoutRef.current = null;
1522
+ }
1523
+ wsSubprojectRefreshTimeoutRef.current = window.setTimeout(() => {
1524
+ wsSubprojectRefreshTimeoutRef.current = null;
1525
+ const refreshSubprojectsFn = refreshSubprojectTaskCountsRef.current;
1526
+ if (refreshSubprojectsFn)
1527
+ void refreshSubprojectsFn();
1528
+ }, 250);
1509
1529
  }
1510
1530
  return;
1511
1531
  }
@@ -1517,23 +1537,49 @@ export function TaskBoardWorkspace() {
1517
1537
  const payload = message?.payload;
1518
1538
  const projectId = payload?.projectId ?? payload?.ProjectId;
1519
1539
  const currentProjectId = selectedProjectIdRef.current;
1520
- const isDirectSubproject = !!projectId && directSubprojectIdsRef.current.has(projectId);
1521
1540
  if (!projectId || !currentProjectId)
1522
1541
  return;
1523
1542
  if (projectId !== currentProjectId) {
1524
- if (!isDirectSubproject)
1543
+ if (!directSubprojectIdsRef.current.has(projectId))
1525
1544
  return;
1526
- queueSubprojectRefresh({
1527
- refreshProjects: messageType !== "task:updated",
1528
- });
1545
+ if (wsSubprojectRefreshTimeoutRef.current !== null) {
1546
+ window.clearTimeout(wsSubprojectRefreshTimeoutRef.current);
1547
+ wsSubprojectRefreshTimeoutRef.current = null;
1548
+ }
1549
+ wsSubprojectRefreshTimeoutRef.current = window.setTimeout(() => {
1550
+ wsSubprojectRefreshTimeoutRef.current = null;
1551
+ const refreshSubprojectsFn = refreshSubprojectTaskCountsRef.current;
1552
+ const refreshProjectsFn = refreshProjectsRef.current;
1553
+ if (!refreshSubprojectsFn)
1554
+ return;
1555
+ void refreshSubprojectsFn();
1556
+ if (refreshProjectsFn)
1557
+ void refreshProjectsFn();
1558
+ }, 250);
1529
1559
  return;
1530
1560
  }
1531
- queueSelectedProjectRefresh({
1532
- refreshTasks: true,
1533
- refreshExecutionState: messageType !== "task:updated",
1534
- refreshDependencies: true,
1535
- refreshProjects: messageType !== "task:updated",
1536
- });
1561
+ if (wsRefreshTimeoutRef.current !== null) {
1562
+ window.clearTimeout(wsRefreshTimeoutRef.current);
1563
+ wsRefreshTimeoutRef.current = null;
1564
+ }
1565
+ wsRefreshTimeoutRef.current = window.setTimeout(() => {
1566
+ wsRefreshTimeoutRef.current = null;
1567
+ const pid = selectedProjectIdRef.current;
1568
+ const refreshProjectsFn = refreshProjectsRef.current;
1569
+ const refreshTasksFn = refreshTasksRef.current;
1570
+ const refreshExecutionStateFn = refreshExecutionStateRef.current;
1571
+ const refreshDepsFn = refreshDependenciesRef.current;
1572
+ if (!pid ||
1573
+ !refreshTasksFn ||
1574
+ !refreshDepsFn ||
1575
+ !refreshExecutionStateFn)
1576
+ return;
1577
+ void refreshTasksFn(pid);
1578
+ void refreshExecutionStateFn(pid);
1579
+ void refreshDepsFn(pid);
1580
+ if (refreshProjectsFn)
1581
+ void refreshProjectsFn();
1582
+ }, 250);
1537
1583
  });
1538
1584
  return () => {
1539
1585
  unsubscribe();
@@ -1542,7 +1588,7 @@ export function TaskBoardWorkspace() {
1542
1588
  // Clearing it here was the bug: editContext changes caused cleanup to
1543
1589
  // cancel the pending debounced refresh before it could fire.
1544
1590
  };
1545
- }, [editContext, isProjectWithinSelectedTree, queueSelectedProjectRefresh, queueSubprojectRefresh]);
1591
+ }, [editContext]);
1546
1592
  // Keep project dropdown in sync when projects are created elsewhere (e.g. by agents).
1547
1593
  useEffect(() => {
1548
1594
  const addListener = editContext?.addSocketMessageListener;
@@ -1566,15 +1612,6 @@ export function TaskBoardWorkspace() {
1566
1612
  window.clearTimeout(wsSubprojectRefreshTimeoutRef.current);
1567
1613
  wsSubprojectRefreshTimeoutRef.current = null;
1568
1614
  }
1569
- wsRefreshQueuedRef.current = false;
1570
- wsRefreshRunningRef.current = false;
1571
- wsRefreshNeedsTasksRef.current = false;
1572
- wsRefreshNeedsExecutionStateRef.current = false;
1573
- wsRefreshNeedsDependenciesRef.current = false;
1574
- wsRefreshNeedsProjectsRef.current = false;
1575
- wsSubprojectRefreshQueuedRef.current = false;
1576
- wsSubprojectRefreshRunningRef.current = false;
1577
- wsSubprojectRefreshNeedsProjectsRef.current = false;
1578
1615
  if (wizardCloseTransitionTimeoutRef.current !== null) {
1579
1616
  window.clearTimeout(wizardCloseTransitionTimeoutRef.current);
1580
1617
  wizardCloseTransitionTimeoutRef.current = null;
@@ -1670,7 +1707,7 @@ export function TaskBoardWorkspace() {
1670
1707
  return (_jsx("div", { className: "flex h-full min-h-[240px] items-center justify-center rounded-lg border border-dashed border-slate-200 bg-slate-50/70", "data-testid": "taskboard-project-loading-state", children: _jsxs("div", { className: "text-muted-foreground flex flex-col items-center gap-3 text-sm", children: [_jsx(Loader2, { className: "h-6 w-6 animate-spin" }), _jsx("div", { children: "Loading project..." })] }) }));
1671
1708
  }
1672
1709
  if (!selectedProjectId || (!selectedProject && !isProjectsLoading)) {
1673
- return (_jsx("div", { className: "flex min-h-full w-full flex-col p-4 md:p-8", children: _jsx("div", { className: "mx-auto flex h-full min-h-0 w-full max-w-7xl flex-1", children: _jsx(ProjectOverviewContent, { nav: nav }) }) }));
1710
+ return (_jsx("div", { className: "flex min-h-full w-full flex-col p-4 md:p-8", children: _jsx("div", { className: "mx-auto flex h-full w-full max-w-7xl flex-1 min-h-0", children: _jsx(ProjectOverviewContent, { nav: nav }) }) }));
1674
1711
  }
1675
1712
  if (isWizardMode) {
1676
1713
  return (_jsx(WizardView, { projectId: selectedProjectId, tasks: tasksWithDisplayAssignees, projectTasksLoading: isTasksLoading, dependencies: dependencies, subprojectTaskLists: wizardSubprojectTaskLists, subprojectTasksLoading: subprojectCountsLoading, selectedTaskId: wizardDisplayedTask?.taskId ?? null, onSelectTask: (id) => {
@@ -1684,7 +1721,7 @@ export function TaskBoardWorkspace() {
1684
1721
  }, projectId: selectedProjectId, permission: selectedProject?.permission })] }));
1685
1722
  }
1686
1723
  if (isGraphView) {
1687
- 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, selectedProjectStatus: selectedProject?.project.status, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, canEditProjectStatus: selectedProject?.permission === "Owner", savingProjectStatus: savingProjectStatus, onStatusChange: handleProjectStatusChange, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, loading: subprojectCountsLoading, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canEditProperties: selectedProject?.permission === "Owner" }), sharedViewSwitcher, _jsx("div", { className: "min-h-0 flex-1", children: _jsx(DependencyGraphView, { projectId: selectedProjectId, tasks: tasksWithDisplayAssignees, dependencies: dependencies, orientation: "horizontal", autoLayoutStrategy: "hierarchy", showOrientationToggle: true, savedLayout: graphLayout, selectedTaskId: selectedTaskId, onSelectTask: (id) => setTaskSelection(id, { source: "user" }), permission: selectedProject?.permission, onLayoutSaved: (layout) => setGraphLayout(layout && selectedProjectId
1724
+ 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, selectedProjectStatus: selectedProject?.project.status, selectedProjectCostUsed: selectedProjectCumulativeCostUsed, selectedProjectCostLimit: selectedProject?.project.costLimit ?? null, canEditProjectStatus: selectedProject?.permission === "Owner", savingProjectStatus: savingProjectStatus, onStatusChange: handleProjectStatusChange, selectedProjectTaskCounts: selectedProjectTaskCounts, subprojects: directSubprojects, taskCountsByProjectId: subprojectTaskCounts, loading: subprojectCountsLoading, parentProjectId: parentProject?.project.projectId ?? null, parentProjectTitle: parentProject?.project.title ?? null, onSelectProject: handleSelectProject, canEditProperties: selectedProject?.permission === "Owner" }), sharedViewSwitcher, _jsx("div", { className: "min-h-0 flex-1", children: _jsx(DependencyGraphView, { projectId: selectedProjectId, tasks: tasksWithDisplayAssignees, dependencies: dependencies, orientation: "horizontal", savedLayout: graphLayout, selectedTaskId: selectedTaskId, onSelectTask: (id) => setTaskSelection(id, { source: "user" }), permission: selectedProject?.permission, onLayoutSaved: (layout) => setGraphLayout(layout && selectedProjectId
1688
1725
  ? {
1689
1726
  projectId: selectedProjectId,
1690
1727
  ...layout,
@@ -1726,7 +1763,7 @@ export function TaskBoardWorkspace() {
1726
1763
  setTaskSelection,
1727
1764
  refreshVisibleTaskData,
1728
1765
  ]);
1729
- const taskDetailPanel = useMemo(() => (_jsx(TaskDetailPanel, { project: selectedTaskProject, projects: projects, task: selectedTask, allTasks: taskSelectionUniverse, dependencies: dependencies, onTaskChanged: () => {
1766
+ const taskDetailPanel = useMemo(() => (_jsx(TaskDetailPanel, { project: selectedTaskProject, task: selectedTask, allTasks: taskSelectionUniverse, dependencies: dependencies, onTaskChanged: () => {
1730
1767
  void refreshVisibleTaskData();
1731
1768
  }, onSelectTask: (taskId) => setTaskSelection(taskId, { source: "user" }), onClose: () => setTaskSelection(null), variant: "panel" })), [
1732
1769
  selectedTaskProject,
@@ -1768,6 +1805,161 @@ export function TaskBoardWorkspace() {
1768
1805
  selectedTask,
1769
1806
  handleReopenSelectedTask,
1770
1807
  ]);
1808
+ const previewItemVersion = useMemo(() => {
1809
+ if (!currentItemDescriptor)
1810
+ return null;
1811
+ return currentItemDescriptor.version === 0
1812
+ ? (editContext?.item?.version ?? null)
1813
+ : currentItemDescriptor.version;
1814
+ }, [currentItemDescriptor, editContext?.item?.version]);
1815
+ const contextItemOptions = useMemo(() => {
1816
+ return agentContextItems.map((contextItem) => {
1817
+ const key = `${contextItem.id}|${contextItem.language}|${contextItem.version}`;
1818
+ const name = contextItemNamesByKey[key] || contextItem.name || contextItem.id;
1819
+ const path = contextItemPathsByKey[key] || contextItem.path || "";
1820
+ return {
1821
+ value: key,
1822
+ label: name,
1823
+ description: `${path || contextItem.id} (${contextItem.language}/v${contextItem.version})`,
1824
+ };
1825
+ });
1826
+ }, [agentContextItems, contextItemNamesByKey, contextItemPathsByKey]);
1827
+ const selectedContextItemValue = useMemo(() => {
1828
+ if (currentItemKey)
1829
+ return currentItemKey;
1830
+ const first = agentContextItems[0];
1831
+ if (!first)
1832
+ return "";
1833
+ return `${first.id}|${first.language}|${first.version}`;
1834
+ }, [currentItemKey, agentContextItems]);
1835
+ const hasMultipleContextItems = contextItemOptions.length > 1;
1836
+ useEffect(() => {
1837
+ if (!currentAgentId) {
1838
+ setAgentContextItems([]);
1839
+ return;
1840
+ }
1841
+ let cancelled = false;
1842
+ getAgent(currentAgentId)
1843
+ .then((agent) => {
1844
+ if (cancelled)
1845
+ return;
1846
+ const rawContext = agent?.agentContext;
1847
+ if (!rawContext) {
1848
+ setAgentContextItems([]);
1849
+ return;
1850
+ }
1851
+ let parsed = null;
1852
+ try {
1853
+ parsed = JSON.parse(rawContext);
1854
+ }
1855
+ catch {
1856
+ parsed = null;
1857
+ }
1858
+ const items = (parsed?.items || []).filter((x) => !!x?.id && !!x?.language && typeof x?.version === "number");
1859
+ setAgentContextItems(items);
1860
+ })
1861
+ .catch(() => {
1862
+ if (cancelled)
1863
+ return;
1864
+ setAgentContextItems([]);
1865
+ });
1866
+ return () => {
1867
+ cancelled = true;
1868
+ };
1869
+ }, [currentAgentId]);
1870
+ useEffect(() => {
1871
+ if (!editContext || agentContextItems.length === 0) {
1872
+ setContextItemNamesByKey({});
1873
+ setContextItemPathsByKey({});
1874
+ return;
1875
+ }
1876
+ let cancelled = false;
1877
+ Promise.all(agentContextItems.map(async (contextItem) => {
1878
+ const key = `${contextItem.id}|${contextItem.language}|${contextItem.version}`;
1879
+ const descriptor = {
1880
+ id: contextItem.id,
1881
+ language: contextItem.language,
1882
+ version: contextItem.version,
1883
+ };
1884
+ const item = await editContext.itemsRepository.getItem(descriptor);
1885
+ return {
1886
+ key,
1887
+ name: item?.name || contextItem.name || "",
1888
+ path: item?.path || contextItem.path || "",
1889
+ };
1890
+ }))
1891
+ .then((results) => {
1892
+ if (cancelled)
1893
+ return;
1894
+ const nextNames = {};
1895
+ const nextPaths = {};
1896
+ for (const result of results) {
1897
+ nextNames[result.key] = result.name;
1898
+ nextPaths[result.key] = result.path;
1899
+ }
1900
+ setContextItemNamesByKey(nextNames);
1901
+ setContextItemPathsByKey(nextPaths);
1902
+ })
1903
+ .catch(() => {
1904
+ if (cancelled)
1905
+ return;
1906
+ setContextItemNamesByKey({});
1907
+ setContextItemPathsByKey({});
1908
+ });
1909
+ return () => {
1910
+ cancelled = true;
1911
+ };
1912
+ }, [editContext, agentContextItems]);
1913
+ const handleSelectContextItem = useCallback((value) => {
1914
+ const [id, language, versionText] = value.split("|");
1915
+ const version = Number(versionText);
1916
+ if (!id || !language || Number.isNaN(version) || !editContext?.loadItem)
1917
+ return;
1918
+ void editContext.loadItem({ id, language, version });
1919
+ }, [editContext]);
1920
+ useEffect(() => {
1921
+ if (!editContext || !currentItemDescriptor) {
1922
+ setPreviewItemName("");
1923
+ setPreviewItemPath("");
1924
+ return;
1925
+ }
1926
+ const activeItem = editContext.item;
1927
+ const itemMatchesDescriptor = activeItem?.id === currentItemDescriptor.id &&
1928
+ activeItem?.language === currentItemDescriptor.language;
1929
+ if (itemMatchesDescriptor) {
1930
+ setPreviewItemName(activeItem?.name || "");
1931
+ setPreviewItemPath(activeItem?.path || "");
1932
+ return;
1933
+ }
1934
+ let cancelled = false;
1935
+ editContext.itemsRepository
1936
+ .getItem(currentItemDescriptor)
1937
+ .then((item) => {
1938
+ if (cancelled)
1939
+ return;
1940
+ setPreviewItemName(item?.name || "");
1941
+ setPreviewItemPath(item?.path || "");
1942
+ })
1943
+ .catch(() => {
1944
+ if (cancelled)
1945
+ return;
1946
+ setPreviewItemName("");
1947
+ setPreviewItemPath("");
1948
+ });
1949
+ return () => {
1950
+ cancelled = true;
1951
+ };
1952
+ }, [
1953
+ editContext,
1954
+ currentItemDescriptor,
1955
+ currentItemDescriptor?.id,
1956
+ currentItemDescriptor?.language,
1957
+ currentItemDescriptor?.version,
1958
+ editContext?.item?.id,
1959
+ editContext?.item?.language,
1960
+ editContext?.item?.name,
1961
+ editContext?.item?.path,
1962
+ ]);
1771
1963
  const itemPreviewPanel = useMemo(() => {
1772
1964
  const hasCurrentItem = !!currentItemDescriptor;
1773
1965
  const previewSidebar = (_jsx("div", { className: "relative z-10 flex h-full min-h-0 flex-col bg-white", children: _jsx("div", { className: "min-h-0 flex-1", children: _jsx(MainContentTree, { mode: "normal" }) }) }));
@@ -1940,7 +2132,7 @@ export function TaskBoardWorkspace() {
1940
2132
  id: "agent",
1941
2133
  label: "Agent",
1942
2134
  content: null,
1943
- disabled: !displayAgentId || shouldHideAgentPanelForSelectedTask,
2135
+ disabled: !currentAgentId || shouldHideAgentPanelForSelectedTask,
1944
2136
  },
1945
2137
  ...(showEditorPanel
1946
2138
  ? [{ id: "editor", label: "Editor", content: null }]
@@ -1966,10 +2158,7 @@ export function TaskBoardWorkspace() {
1966
2158
  } }), _jsx(ProjectSettingsDialog, { open: !!settingsProjectId, onOpenChange: (open) => {
1967
2159
  if (!open)
1968
2160
  setSettingsProjectId(null);
1969
- }, projectId: settingsProjectId, selectedProject: settingsProject, projects: projects, onChanged: async () => {
1970
- await refreshProjects();
1971
- await refreshVisibleTaskData();
1972
- } }), _jsx(AssignAgentDialog, { open: assignAgentDialogOpen, task: selectedTask, onOpenChange: setAssignAgentDialogOpen, onAssigned: async () => {
2161
+ }, projectId: settingsProjectId, selectedProject: settingsProject, onChanged: refreshProjects }), _jsx(AssignAgentDialog, { open: assignAgentDialogOpen, task: selectedTask, onOpenChange: setAssignAgentDialogOpen, onAssigned: async () => {
1973
2162
  if (!selectedProjectId)
1974
2163
  return;
1975
2164
  await refreshTasks(selectedProjectId);