@parhelia/core 0.1.12393 → 0.1.12404

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 (99) hide show
  1. package/dist/components/ui/input.d.ts +1 -1
  2. package/dist/components/ui/input.js +5 -3
  3. package/dist/components/ui/input.js.map +1 -1
  4. package/dist/editor/FieldHistory.js +47 -17
  5. package/dist/editor/FieldHistory.js.map +1 -1
  6. package/dist/editor/PictureCropper.js +9 -4
  7. package/dist/editor/PictureCropper.js.map +1 -1
  8. package/dist/editor/PictureEditor.js +12 -13
  9. package/dist/editor/PictureEditor.js.map +1 -1
  10. package/dist/editor/ai/AgentTerminal.js +136 -23
  11. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  12. package/dist/editor/ai/AgentTerminalStatusBar.d.ts +5 -1
  13. package/dist/editor/ai/AgentTerminalStatusBar.js +182 -22
  14. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  15. package/dist/editor/ai/ContentInspectorPopover.js +105 -24
  16. package/dist/editor/ai/ContentInspectorPopover.js.map +1 -1
  17. package/dist/editor/ai/ContextInfoBar.d.ts +2 -1
  18. package/dist/editor/ai/ContextInfoBar.js +9 -6
  19. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  20. package/dist/editor/ai/agentDiagnostics.d.ts +36 -0
  21. package/dist/editor/ai/agentDiagnostics.js +120 -0
  22. package/dist/editor/ai/agentDiagnostics.js.map +1 -0
  23. package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +2 -1
  24. package/dist/editor/ai/dialogs/QuestionnaireInline.js +452 -63
  25. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  26. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +27 -5
  27. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  28. package/dist/editor/client/EditorShell.js +8 -0
  29. package/dist/editor/client/EditorShell.js.map +1 -1
  30. package/dist/editor/client/editContext.d.ts +6 -0
  31. package/dist/editor/client/editContext.js.map +1 -1
  32. package/dist/editor/client/hooks/useEditorWebSocket.d.ts +3 -0
  33. package/dist/editor/client/hooks/useEditorWebSocket.js +66 -1
  34. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  35. package/dist/editor/client/socketDiagnostics.d.ts +19 -0
  36. package/dist/editor/client/socketDiagnostics.js +32 -0
  37. package/dist/editor/client/socketDiagnostics.js.map +1 -0
  38. package/dist/editor/pictureRawValue.d.ts +3 -0
  39. package/dist/editor/pictureRawValue.js +30 -0
  40. package/dist/editor/pictureRawValue.js.map +1 -0
  41. package/dist/editor/reviews/CreateReviewDetailsStep.d.ts +1 -1
  42. package/dist/editor/reviews/CreateReviewDialog.js +3 -2
  43. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  44. package/dist/editor/services/agentService.d.ts +57 -0
  45. package/dist/editor/services/agentService.js +30 -0
  46. package/dist/editor/services/agentService.js.map +1 -1
  47. package/dist/editor/services/aiService.d.ts +5 -0
  48. package/dist/editor/services/aiService.js.map +1 -1
  49. package/dist/editor/services/contentService.js.map +1 -1
  50. package/dist/editor/settings/panels/AgentProfileConfigPanel.js +1 -0
  51. package/dist/editor/settings/panels/AgentProfileConfigPanel.js.map +1 -1
  52. package/dist/editor/settings/panels/AgentProfileEditorPanel.d.ts +14 -0
  53. package/dist/editor/settings/panels/AgentProfileEditorPanel.js +7 -0
  54. package/dist/editor/settings/panels/AgentProfileEditorPanel.js.map +1 -0
  55. package/dist/editor/settings/panels/AgentsPanel.js +2 -2
  56. package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
  57. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +132 -82
  58. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  59. package/dist/editor/ui/ItemCollectionEditor.d.ts +2 -1
  60. package/dist/editor/ui/ItemCollectionEditor.js +70 -27
  61. package/dist/editor/ui/ItemCollectionEditor.js.map +1 -1
  62. package/dist/editor/ui/TreeListSelector.d.ts +4 -1
  63. package/dist/editor/ui/TreeListSelector.js +2 -2
  64. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  65. package/dist/revision.d.ts +2 -2
  66. package/dist/revision.js +2 -2
  67. package/dist/task-board/TaskBoardWorkspace.js +11 -18
  68. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  69. package/dist/task-board/components/ItemCollectionEditorDialog.js +28 -17
  70. package/dist/task-board/components/ItemCollectionEditorDialog.js.map +1 -1
  71. package/dist/task-board/components/ProjectDashboard.d.ts +4 -0
  72. package/dist/task-board/components/ProjectDashboard.js +1 -1
  73. package/dist/task-board/components/ProjectDashboard.js.map +1 -1
  74. package/dist/task-board/components/ProjectListContent.d.ts +1 -1
  75. package/dist/task-board/components/ProjectListContent.js +4 -1
  76. package/dist/task-board/components/ProjectListContent.js.map +1 -1
  77. package/dist/task-board/components/ProjectOverviewContent.d.ts +17 -0
  78. package/dist/task-board/components/ProjectOverviewContent.js +134 -0
  79. package/dist/task-board/components/ProjectOverviewContent.js.map +1 -0
  80. package/dist/task-board/components/ProjectSelector.d.ts +1 -1
  81. package/dist/task-board/components/ProjectSelector.js +1 -1
  82. package/dist/task-board/components/ProjectSelector.js.map +1 -1
  83. package/dist/task-board/components/TaskAssigneePicker.d.ts +3 -2
  84. package/dist/task-board/components/TaskAssigneePicker.js +10 -6
  85. package/dist/task-board/components/TaskAssigneePicker.js.map +1 -1
  86. package/dist/task-board/components/TaskDetailPanel.js +59 -9
  87. package/dist/task-board/components/TaskDetailPanel.js.map +1 -1
  88. package/dist/task-board/services/taskService.d.ts +5 -1
  89. package/dist/task-board/services/taskService.js +11 -0
  90. package/dist/task-board/services/taskService.js.map +1 -1
  91. package/dist/task-board/taskBoardNavStore.d.ts +3 -1
  92. package/dist/task-board/taskBoardNavStore.js.map +1 -1
  93. package/dist/task-board/types.d.ts +30 -0
  94. package/dist/task-board/utils/taskDependencyOrdering.d.ts +3 -0
  95. package/dist/task-board/utils/taskDependencyOrdering.js +64 -0
  96. package/dist/task-board/utils/taskDependencyOrdering.js.map +1 -0
  97. package/dist/task-board/views/WizardView.js +5 -15
  98. package/dist/task-board/views/WizardView.js.map +1 -1
  99. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
3
- import { AlertCircle, ChevronDown, CornerDownRight, GitBranch, Link2, Plus, RefreshCw, Search, Settings2, Shield, Trash2, X, } from "lucide-react";
3
+ import { AlertCircle, ChevronDown, CopyPlus, CornerDownRight, GitBranch, Link2, Plus, RefreshCw, Search, Settings2, Shield, Trash2, X, } from "lucide-react";
4
4
  import { toast } from "sonner";
5
5
  import { Button } from "../../../components/ui/button";
6
6
  import { Dialog, DialogContent, DialogFooter, } from "../../../components/ui/dialog";
@@ -14,12 +14,15 @@ import { Badge } from "../../../components/ui/badge";
14
14
  import { Popover, PopoverContent, PopoverTrigger, } from "../../../components/ui/popover";
15
15
  import { Splitter } from "../../ui/Splitter";
16
16
  import { DependencyGraphView } from "../../../task-board/views/DependencyGraphView";
17
+ import { TaskAssigneePicker } from "../../../task-board/components/TaskAssigneePicker";
17
18
  import { deleteProjectTemplate, getProjectTemplates, getProjectTemplateAgent, resetProjectTemplateAgent, upsertProjectTemplate, } from "../../../task-board/services/taskService";
18
19
  import { getAiProfilesErrorMessage, loadAiProfiles, } from "../../services/aiService";
19
20
  import { updateAgentContext } from "../../services/agentService";
20
21
  import { useEditContext } from "../../client/editContext";
22
+ import { ItemNameDialog, } from "../../ui/ItemNameDialogNew";
21
23
  import { isTypingEventTarget } from "../../utils/keyboardNavigation";
22
24
  import { cn } from "../../../lib/utils";
25
+ import { AgentProfileEditorPanel, } from "./AgentProfileEditorPanel";
23
26
  import { ProjectTemplateAgentPanel } from "./ProjectTemplateAgentPanel";
24
27
  import { useSearchParams } from "next/navigation";
25
28
  /** Query param for deep-linking the selected project template (settings reload). */
@@ -31,12 +34,6 @@ const PRIORITY_OPTIONS = [
31
34
  { value: "High", label: "High" },
32
35
  { value: "Critical", label: "Critical" },
33
36
  ];
34
- const ASSIGNEE_TYPE_OPTIONS = [
35
- { value: "", label: "Unassigned" },
36
- { value: "Agent", label: "Agent profile" },
37
- { value: "Human", label: "Human user" },
38
- { value: "Role", label: "Role" },
39
- ];
40
37
  const PROJECT_TEMPLATE_AUTOSAVE_DEBOUNCE_MS = 1200;
41
38
  function cloneTemplate(template) {
42
39
  return normalizeProjectTemplate(JSON.parse(JSON.stringify(template)));
@@ -330,6 +327,8 @@ export function ProjectTemplatesPanel() {
330
327
  const [templateAgentLoading, setTemplateAgentLoading] = useState(false);
331
328
  const [templateAgentResetting, setTemplateAgentResetting] = useState(false);
332
329
  const [templateAgentError, setTemplateAgentError] = useState(null);
330
+ const [duplicatingAgentProfile, setDuplicatingAgentProfile] = useState(false);
331
+ const [editingAgentProfile, setEditingAgentProfile] = useState(null);
333
332
  const autosaveTimeoutRef = useRef(null);
334
333
  const lastPersistedSignatureRef = useRef(null);
335
334
  const latestDraftSignatureRef = useRef(null);
@@ -373,6 +372,7 @@ export function ProjectTemplatesPanel() {
373
372
  draftTemplateRef.current = clonedTemplate;
374
373
  setSelectedTemplateId(template?.id ?? null);
375
374
  setDraftTemplate(clonedTemplate);
375
+ setEditingAgentProfile(null);
376
376
  setSelectedTaskId(getSelectedTaskIdForTemplate(clonedTemplate, preferredTaskId));
377
377
  setIsDirty(false);
378
378
  setSaveError(null);
@@ -501,23 +501,6 @@ export function ProjectTemplatesPanel() {
501
501
  return draftTemplate.taskTemplates.filter((task) => task.id !== selectedTaskId &&
502
502
  !selectedTask.dependencyIds.includes(task.id));
503
503
  }, [draftTemplate, selectedTaskId, selectedTask]);
504
- const parentTaskSelectOptions = useMemo(() => {
505
- if (!draftTemplate || !selectedTaskId) {
506
- return [{ value: "", label: "None" }];
507
- }
508
- const others = draftTemplate.taskTemplates.filter((task) => task.id !== selectedTaskId);
509
- return [
510
- { value: "", label: "None" },
511
- ...[...others]
512
- .sort((a, b) => (a.title || "").localeCompare(b.title || "", undefined, {
513
- sensitivity: "base",
514
- }))
515
- .map((task) => ({
516
- value: task.id,
517
- label: task.title?.trim() || "Untitled Task",
518
- })),
519
- ];
520
- }, [draftTemplate, selectedTaskId]);
521
504
  const headerStatusBadge = useMemo(() => {
522
505
  if (saveError) {
523
506
  return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "error", className: "inline-flex min-w-[84px] justify-center rounded bg-red-100 px-1.5 py-0.5 whitespace-nowrap text-red-700", children: "Save failed" }));
@@ -529,11 +512,33 @@ export function ProjectTemplatesPanel() {
529
512
  }, [isDirty, saveError, saving]);
530
513
  const graphTasks = useMemo(() => buildTemplateTaskItems(draftTemplate, aiProfilesById), [aiProfilesById, draftTemplate]);
531
514
  const graphDependencies = useMemo(() => buildTemplateDependencies(draftTemplate), [draftTemplate]);
532
- const profileOptions = useMemo(() => aiProfiles.map((profile) => ({
533
- value: profile.id,
534
- label: profile.displayTitle || profile.name,
535
- description: profile.description || undefined,
536
- })), [aiProfiles]);
515
+ const selectedAgentProfileId = selectedTask?.assigneeType === "Agent"
516
+ ? selectedTask.assigneeId ?? selectedTask.agentProfileId ?? null
517
+ : null;
518
+ const selectedAgentProfile = useMemo(() => selectedAgentProfileId
519
+ ? aiProfilesById[selectedAgentProfileId.toLowerCase()] ?? null
520
+ : null, [aiProfilesById, selectedAgentProfileId]);
521
+ const selectedTaskAssigneeDisplayValue = selectedTask?.assigneeType === "Agent"
522
+ ? selectedAgentProfile?.displayTitle ||
523
+ selectedAgentProfile?.name ||
524
+ selectedTask.assigneeId ||
525
+ selectedTask.agentProfileId ||
526
+ null
527
+ : selectedTask?.assigneeId ?? null;
528
+ const handleEditAssignedAgentProfile = useCallback(() => {
529
+ if (!selectedAgentProfileId)
530
+ return;
531
+ setEditingAgentProfile({
532
+ id: selectedAgentProfileId,
533
+ name: selectedAgentProfile?.displayTitle ||
534
+ selectedAgentProfile?.name ||
535
+ selectedTask?.title ||
536
+ "Agent Profile",
537
+ });
538
+ }, [selectedAgentProfile, selectedAgentProfileId, selectedTask?.title]);
539
+ const handleCloseEditingAgentProfile = useCallback(() => {
540
+ setEditingAgentProfile(null);
541
+ }, []);
537
542
  const syncProjectTemplateAgentContext = useCallback(async (agentId, template, selectedTaskTemplateId) => {
538
543
  try {
539
544
  await updateAgentContext(agentId, buildProjectTemplateAgentContext(template, selectedTaskTemplateId));
@@ -621,6 +626,92 @@ export function ProjectTemplatesPanel() {
621
626
  taskTemplates: currentTemplate.taskTemplates.map((task) => task.id === selectedTaskId ? updater({ ...task }) : task),
622
627
  }));
623
628
  }, [applyDraftChange, selectedTaskId]);
629
+ const handleDuplicateAssignedAgentProfile = useCallback(async () => {
630
+ if (!editContext || !selectedAgentProfileId)
631
+ return;
632
+ try {
633
+ setDuplicatingAgentProfile(true);
634
+ const sourceDescriptor = {
635
+ id: selectedAgentProfileId,
636
+ language: editContext.item?.language ??
637
+ editContext.currentItemDescriptor?.language ??
638
+ "en",
639
+ version: 0,
640
+ };
641
+ const sourceItem = await editContext.itemsRepository.getItem(sourceDescriptor);
642
+ if (!sourceItem) {
643
+ toast.error("Unable to load the selected agent profile");
644
+ return;
645
+ }
646
+ const newName = await editContext.openDialog(ItemNameDialog, {
647
+ name: `Copy of ${sourceItem.name}`,
648
+ title: "Duplicate Agent Profile",
649
+ message: "Enter a name for the duplicated agent profile:",
650
+ parentItem: { ...sourceItem.descriptor, id: sourceItem.parentId },
651
+ itemId: sourceItem.id,
652
+ });
653
+ if (!newName) {
654
+ return;
655
+ }
656
+ const duplicatedProfile = await editContext.operations.duplicateItem(sourceItem.descriptor, {
657
+ id: sourceItem.parentId,
658
+ language: sourceItem.language,
659
+ version: 0,
660
+ }, newName);
661
+ if (!duplicatedProfile) {
662
+ toast.error("Unable to duplicate the selected agent profile");
663
+ return;
664
+ }
665
+ try {
666
+ const duplicatedItem = await editContext.itemsRepository.getItem({
667
+ id: duplicatedProfile.id,
668
+ language: duplicatedProfile.language ?? sourceItem.language,
669
+ version: duplicatedProfile.version ?? 0,
670
+ });
671
+ const displayTitleField = duplicatedItem?.fields.find((field) => {
672
+ const fieldName = field.name?.trim().toLowerCase();
673
+ const displayName = field.displayName?.trim().toLowerCase();
674
+ return fieldName === "display title" || displayName === "display title";
675
+ });
676
+ if (displayTitleField) {
677
+ await editContext.operations.editField({
678
+ field: displayTitleField.descriptor,
679
+ rawValue: newName,
680
+ refresh: "immediate",
681
+ });
682
+ }
683
+ }
684
+ catch {
685
+ // Best-effort only; duplication itself already succeeded.
686
+ }
687
+ updateSelectedTask((currentTask) => ({
688
+ ...currentTask,
689
+ assigneeType: "Agent",
690
+ assigneeId: duplicatedProfile.id,
691
+ agentProfileId: duplicatedProfile.id,
692
+ }));
693
+ setEditingAgentProfile({
694
+ id: duplicatedProfile.id,
695
+ name: duplicatedProfile.name || newName,
696
+ });
697
+ try {
698
+ const refreshedProfiles = await loadAiProfiles();
699
+ setAiProfiles(refreshedProfiles || []);
700
+ setProfilesError(null);
701
+ }
702
+ catch (loadError) {
703
+ setProfilesError(getAiProfilesErrorMessage(loadError));
704
+ }
705
+ }
706
+ catch (duplicateError) {
707
+ toast.error(duplicateError instanceof Error
708
+ ? `Failed to duplicate agent profile: ${duplicateError.message}`
709
+ : "Failed to duplicate agent profile");
710
+ }
711
+ finally {
712
+ setDuplicatingAgentProfile(false);
713
+ }
714
+ }, [editContext, selectedAgentProfileId, updateSelectedTask]);
624
715
  const currentSaveRequest = useMemo(() => {
625
716
  if (!draftTemplate || !draftTemplate.name.trim()) {
626
717
  return null;
@@ -1224,65 +1315,20 @@ export function ProjectTemplatesPanel() {
1224
1315
  content: (_jsx("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", "data-testid": "project-template-task-detail-pane", children: !selectedTask ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: _jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-xs text-gray-500", children: "Select a task node to edit its details." }) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 border-b border-gray-100 bg-white px-4 pb-3 pt-4", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", "data-testid": "project-template-selected-task-title", children: selectedTask.title || "Untitled Task" }), _jsx("div", { className: "text-xs text-gray-500", children: "Configure task details, assignment, and dependencies." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDeleteSelectedTask, "data-testid": "project-template-remove-task-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), "Remove Task"] })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 pb-4 pt-3", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Task title" }), _jsx(Input, { value: selectedTask.title, onChange: (event) => updateSelectedTask((currentTask) => ({
1225
1316
  ...currentTask,
1226
1317
  title: event.target.value,
1227
- })), placeholder: "Task title", className: "text-xs md:text-xs", "data-testid": "project-template-task-title-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Parent task" }), _jsx(Select, { className: "text-xs", value: selectedTask.parentTaskId ?? "", onValueChange: (value) => {
1228
- const newParentId = value.trim() || null;
1229
- updateSelectedTask((currentTask) => {
1230
- const oldParent = currentTask.parentTaskId ?? null;
1231
- let deps = [
1232
- ...(currentTask.dependencyIds ?? []),
1233
- ];
1234
- if (oldParent &&
1235
- oldParent !== newParentId) {
1236
- deps = deps.filter((id) => id !== oldParent);
1237
- }
1238
- if (newParentId) {
1239
- if (!deps.includes(newParentId)) {
1240
- deps = [...deps, newParentId];
1241
- }
1242
- }
1243
- else if (oldParent) {
1244
- deps = deps.filter((id) => id !== oldParent);
1245
- }
1246
- return {
1247
- ...currentTask,
1248
- parentTaskId: newParentId,
1249
- dependencyIds: deps,
1250
- };
1251
- });
1252
- }, options: parentTaskSelectOptions, placeholder: "No parent", "data-testid": "project-template-task-parent-select" }), _jsx("p", { className: "text-[11px] leading-snug text-gray-500", children: "Marks this task as a child in the template; the parent is also added as a blocking dependency unless already listed." })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: selectedTask.description ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
1318
+ })), placeholder: "Task title", className: "text-xs md:text-xs", "data-testid": "project-template-task-title-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: selectedTask.description ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
1253
1319
  ...currentTask,
1254
1320
  description: event.target.value,
1255
1321
  })), rows: 4, placeholder: "Describe what this task should accomplish", "data-testid": "project-template-task-description-input" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Priority" }), _jsx(Select, { className: "text-xs", value: selectedTask.priority ?? "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
1256
1322
  ...currentTask,
1257
1323
  priority: value || null,
1258
- })), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Default assignment type" }), _jsx(Select, { className: "text-xs", value: selectedTask.assigneeType ?? "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
1324
+ })), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-xs", children: "Default assignee" }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2 text-xs", onClick: () => void handleEditAssignedAgentProfile(), disabled: !selectedAgentProfileId, "data-testid": "project-template-task-edit-agent-profile-button", children: [_jsx(Settings2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), "Edit Profile"] })) : null] }), _jsx(TaskAssigneePicker, { projectTemplateId: draftTemplate.id, assigneeType: selectedTask.assigneeType ?? null, value: selectedTask.assigneeId ?? null, displayValue: selectedTaskAssigneeDisplayValue, onChange: (next) => updateSelectedTask((currentTask) => ({
1259
1325
  ...currentTask,
1260
- assigneeType: (value ||
1261
- null),
1262
- assigneeId: value
1263
- ? (currentTask.assigneeId ?? null)
1326
+ assigneeType: next?.assigneeType ?? null,
1327
+ assigneeId: next?.assigneeId ?? null,
1328
+ agentProfileId: next?.assigneeType === "Agent"
1329
+ ? next.assigneeId
1264
1330
  : null,
1265
- agentProfileId: value === "Agent"
1266
- ? (currentTask.agentProfileId ?? null)
1267
- : null,
1268
- })), options: ASSIGNEE_TYPE_OPTIONS, "data-testid": "project-template-task-assignee-type-select" })] }), selectedTask.assigneeType === "Agent" ? (_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Agent profile" }), _jsx(Select, { className: "text-xs", value: selectedTask.assigneeId ??
1269
- selectedTask.agentProfileId ??
1270
- "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
1271
- ...currentTask,
1272
- assigneeType: "Agent",
1273
- assigneeId: value || null,
1274
- agentProfileId: value || null,
1275
- })), options: profileOptions, searchable: true, placeholder: "Select an agent profile", "data-testid": "project-template-task-agent-profile-select" }), profilesError && (_jsx("div", { className: "text-xs text-amber-700", children: profilesError }))] })) : selectedTask.assigneeType === "Human" ? (_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Default user identifier" }), _jsx(Input, { value: selectedTask.assigneeId ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
1276
- ...currentTask,
1277
- assigneeType: "Human",
1278
- assigneeId: event.target.value,
1279
- agentProfileId: null,
1280
- })), placeholder: "sitecore\\\\john", className: "text-xs md:text-xs", "data-testid": "project-template-task-user-assignee-input" })] })) : selectedTask.assigneeType === "Role" ? (_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Default role identifier" }), _jsx(Input, { value: selectedTask.assigneeId ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
1281
- ...currentTask,
1282
- assigneeType: "Role",
1283
- assigneeId: event.target.value,
1284
- agentProfileId: null,
1285
- })), placeholder: "sitecore\\\\Author", className: "text-xs md:text-xs", "data-testid": "project-template-task-role-assignee-input" })] })) : null] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-dependencies-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(Shield, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Dependencies"] }), _jsxs(Popover, { open: dependencyPickerOpen, onOpenChange: setDependencyPickerOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", disabled: dependencyAddCandidates.length === 0, "aria-label": "Add dependency", "data-testid": "project-template-add-dependency-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-700", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length ===
1331
+ })), placeholder: "Assign user or agent", buttonClassName: "h-8 w-full justify-between rounded-md bg-white text-xs font-medium", buttonTestId: "project-template-task-assignee-picker" }), profilesError ? (_jsx("div", { className: "text-xs text-amber-700", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-dependencies-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(Shield, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Dependencies"] }), _jsxs(Popover, { open: dependencyPickerOpen, onOpenChange: setDependencyPickerOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", disabled: dependencyAddCandidates.length === 0, "aria-label": "Add dependency", "data-testid": "project-template-add-dependency-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-700", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length ===
1286
1332
  0 ? (_jsx("div", { className: "px-2 py-3 text-center text-xs text-gray-500", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs hover:bg-gray-100", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 truncate font-medium text-gray-900", children: task.title ||
1287
1333
  "Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
1288
1334
  ? "Add more task templates to create dependencies."
@@ -1290,6 +1336,10 @@ export function ProjectTemplatesPanel() {
1290
1336
  },
1291
1337
  ] }) })] })] })] })) : (_jsx("div", { className: "flex h-full items-center justify-center bg-gray-50/40", children: _jsxs("div", { className: "max-w-sm rounded-lg border border-dashed border-gray-300 bg-white p-8 text-center", children: [_jsx(GitBranch, { className: "mx-auto mb-3 h-10 w-10 text-gray-300", strokeWidth: 1.2 }), _jsx("h3", { className: "text-xs font-semibold text-gray-900", children: "Select a project template" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Choose a template from the list or create a new one to start editing." })] }) }));
1292
1338
  const agentPanelContent = (_jsx(ProjectTemplateAgentPanel, { templateName: draftTemplate?.name ?? null, agentId: templateAgentId, loading: templateAgentLoading, resetting: templateAgentResetting, error: templateAgentError, onStartOver: () => void handleResetTemplateAgent() }));
1339
+ const rightPanelContent = editingAgentProfile ? (_jsx(AgentProfileEditorPanel, { agent: editingAgentProfile, onClose: handleCloseEditingAgentProfile, actions: _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleDuplicateAssignedAgentProfile(), disabled: !selectedAgentProfileId ||
1340
+ !editContext ||
1341
+ duplicatingAgentProfile ||
1342
+ editingAgentProfile.id !== selectedAgentProfileId, "data-testid": "project-template-task-duplicate-agent-profile-button", children: [_jsx(CopyPlus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), duplicatingAgentProfile ? "Duplicating..." : "Duplicate"] }) })) : (agentPanelContent);
1293
1343
  const panels = [
1294
1344
  {
1295
1345
  name: "project-template-list",
@@ -1306,7 +1356,7 @@ export function ProjectTemplatesPanel() {
1306
1356
  {
1307
1357
  name: "project-template-agent",
1308
1358
  defaultSize: 460,
1309
- content: agentPanelContent,
1359
+ content: rightPanelContent,
1310
1360
  className: "overflow-hidden",
1311
1361
  collapsible: true,
1312
1362
  },