@parhelia/core 0.3.12788 → 0.3.12798

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 (235) hide show
  1. package/dist/components/ui/LanguageSelector.js +4 -1
  2. package/dist/components/ui/LanguageSelector.js.map +1 -1
  3. package/dist/components/ui/PlaceholderInput.js +16 -2
  4. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  5. package/dist/components/ui/select.js +12 -1
  6. package/dist/components/ui/select.js.map +1 -1
  7. package/dist/editor/FieldHistory.js +14 -4
  8. package/dist/editor/FieldHistory.js.map +1 -1
  9. package/dist/editor/ai/InlineAiDialog.js +35 -8
  10. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  11. package/dist/editor/ai/agentTodoExtraction.d.ts +1 -0
  12. package/dist/editor/ai/agentTodoExtraction.js +3 -0
  13. package/dist/editor/ai/agentTodoExtraction.js.map +1 -1
  14. package/dist/editor/ai/terminal/agentSessionState.js +26 -4
  15. package/dist/editor/ai/terminal/agentSessionState.js.map +1 -1
  16. package/dist/editor/ai/terminal/components/AgentProjectProgress.d.ts +4 -3
  17. package/dist/editor/ai/terminal/components/AgentProjectProgress.js +34 -50
  18. package/dist/editor/ai/terminal/components/AgentProjectProgress.js.map +1 -1
  19. package/dist/editor/ai/terminal/components/AgentTerminalView.d.ts +2 -1
  20. package/dist/editor/ai/terminal/components/AgentTerminalView.js +2 -2
  21. package/dist/editor/ai/terminal/components/AgentTerminalView.js.map +1 -1
  22. package/dist/editor/ai/terminal/components/AgentThinkingIndicator.js +2 -1
  23. package/dist/editor/ai/terminal/components/AgentThinkingIndicator.js.map +1 -1
  24. package/dist/editor/ai/terminal/components/AiResponseMessage.js +55 -41
  25. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  26. package/dist/editor/ai/terminal/components/ApprovalCard.d.ts +1 -0
  27. package/dist/editor/ai/terminal/components/ApprovalCard.js +15 -1
  28. package/dist/editor/ai/terminal/components/ApprovalCard.js.map +1 -1
  29. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.d.ts +24 -0
  30. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.js +97 -0
  31. package/dist/editor/ai/terminal/components/EditFieldToolCallContent.js.map +1 -0
  32. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js +2 -1
  33. package/dist/editor/ai/terminal/components/InitialThinkingSplash.js.map +1 -1
  34. package/dist/editor/ai/terminal/components/TodoListPanel.js +5 -3
  35. package/dist/editor/ai/terminal/components/TodoListPanel.js.map +1 -1
  36. package/dist/editor/ai/terminal/components/ToolCallDisplay.d.ts +4 -1
  37. package/dist/editor/ai/terminal/components/ToolCallDisplay.js +33 -5
  38. package/dist/editor/ai/terminal/components/ToolCallDisplay.js.map +1 -1
  39. package/dist/editor/ai/terminal/components/ValueDiff.d.ts +15 -0
  40. package/dist/editor/ai/terminal/components/ValueDiff.js +21 -0
  41. package/dist/editor/ai/terminal/components/ValueDiff.js.map +1 -0
  42. package/dist/editor/ai/terminal/components/agentThinkingStyles.d.ts +1 -0
  43. package/dist/editor/ai/terminal/components/agentThinkingStyles.js +2 -0
  44. package/dist/editor/ai/terminal/components/agentThinkingStyles.js.map +1 -0
  45. package/dist/editor/ai/terminal/components/toolCallContentRenderers.d.ts +15 -0
  46. package/dist/editor/ai/terminal/components/toolCallContentRenderers.js +12 -0
  47. package/dist/editor/ai/terminal/components/toolCallContentRenderers.js.map +1 -0
  48. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.d.ts +1 -0
  49. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.js +6 -1
  50. package/dist/editor/ai/terminal/useAgentRunResyncSocketHandler.js.map +1 -1
  51. package/dist/editor/ai/terminal/useAgentStreamPipeline.js +1 -0
  52. package/dist/editor/ai/terminal/useAgentStreamPipeline.js.map +1 -1
  53. package/dist/editor/ai/terminal/useAgentTerminalController.js +1 -0
  54. package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
  55. package/dist/editor/ai/terminal/useAgentTerminalViewProps.d.ts +1 -0
  56. package/dist/editor/ai/terminal/useAgentTerminalViewProps.js +2 -1
  57. package/dist/editor/ai/terminal/useAgentTerminalViewProps.js.map +1 -1
  58. package/dist/editor/ai/types.d.ts +1 -0
  59. package/dist/editor/bridge/protocol.d.ts +11 -0
  60. package/dist/editor/bridge/protocol.js +1 -0
  61. package/dist/editor/bridge/protocol.js.map +1 -1
  62. package/dist/editor/client/EditorShell.js +184 -68
  63. package/dist/editor/client/EditorShell.js.map +1 -1
  64. package/dist/editor/client/editContext.d.ts +13 -3
  65. package/dist/editor/client/editContext.js.map +1 -1
  66. package/dist/editor/client/hooks/activeSessionSnapshots.d.ts +2 -0
  67. package/dist/editor/client/hooks/activeSessionSnapshots.js +43 -0
  68. package/dist/editor/client/hooks/activeSessionSnapshots.js.map +1 -0
  69. package/dist/editor/client/hooks/feedbackSocketState.js +3 -0
  70. package/dist/editor/client/hooks/feedbackSocketState.js.map +1 -1
  71. package/dist/editor/client/hooks/operationRefreshDeduplication.d.ts +22 -0
  72. package/dist/editor/client/hooks/operationRefreshDeduplication.js +45 -0
  73. package/dist/editor/client/hooks/operationRefreshDeduplication.js.map +1 -0
  74. package/dist/editor/client/hooks/socketMessageRetention.d.ts +7 -0
  75. package/dist/editor/client/hooks/socketMessageRetention.js +11 -0
  76. package/dist/editor/client/hooks/socketMessageRetention.js.map +1 -0
  77. package/dist/editor/client/hooks/useSocketMessageHandler.js +53 -19
  78. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  79. package/dist/editor/client/hooks/useWorkbox.d.ts +4 -0
  80. package/dist/editor/client/hooks/useWorkbox.js +132 -2
  81. package/dist/editor/client/hooks/useWorkbox.js.map +1 -1
  82. package/dist/editor/client/itemsRepository.d.ts +7 -1
  83. package/dist/editor/client/itemsRepository.js +55 -19
  84. package/dist/editor/client/itemsRepository.js.map +1 -1
  85. package/dist/editor/client/operations.d.ts +7 -3
  86. package/dist/editor/client/operations.js +216 -150
  87. package/dist/editor/client/operations.js.map +1 -1
  88. package/dist/editor/client/pageModelBuilder.js +21 -4
  89. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  90. package/dist/editor/client/sidebarAvailability.d.ts +4 -0
  91. package/dist/editor/client/sidebarAvailability.js +15 -0
  92. package/dist/editor/client/sidebarAvailability.js.map +1 -1
  93. package/dist/editor/client/waitForEditOperationTerminal.js +1 -1
  94. package/dist/editor/client/waitForEditOperationTerminal.js.map +1 -1
  95. package/dist/editor/commands/itemCommands.js +1 -1
  96. package/dist/editor/commands/itemCommands.js.map +1 -1
  97. package/dist/editor/content-tree/TreeSettingsMenu.js +1 -1
  98. package/dist/editor/content-tree/TreeSettingsMenu.js.map +1 -1
  99. package/dist/editor/field-types/RichTextEditorComponent.js +6 -1
  100. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  101. package/dist/editor/field-types/SingleLineText.js +2 -3
  102. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  103. package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +6 -1
  104. package/dist/editor/field-types/richtext/components/ReactSlate.js +91 -5
  105. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  106. package/dist/editor/field-types/singleLineTextValue.d.ts +3 -0
  107. package/dist/editor/field-types/singleLineTextValue.js +9 -0
  108. package/dist/editor/field-types/singleLineTextValue.js.map +1 -0
  109. package/dist/editor/menubar/ItemLanguageVersion.js +1 -1
  110. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  111. package/dist/editor/menubar/VersionSelector.js +18 -6
  112. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  113. package/dist/editor/menubar/WorkflowButton.js +8 -3
  114. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  115. package/dist/editor/menubar/toolbar-sections/EditControls.js +1 -1
  116. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  117. package/dist/editor/notifications/NotificationCenter.js +26 -7
  118. package/dist/editor/notifications/NotificationCenter.js.map +1 -1
  119. package/dist/editor/notifications/useNotifications.d.ts +3 -1
  120. package/dist/editor/notifications/useNotifications.js +27 -9
  121. package/dist/editor/notifications/useNotifications.js.map +1 -1
  122. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +18 -0
  123. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -1
  124. package/dist/editor/page-editor-chrome/DragInsertionOverlay.js +54 -19
  125. package/dist/editor/page-editor-chrome/DragInsertionOverlay.js.map +1 -1
  126. package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js +30 -8
  127. package/dist/editor/page-editor-chrome/InlineFieldHistoryButton.js.map +1 -1
  128. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js +30 -8
  129. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js.map +1 -1
  130. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.d.ts +4 -0
  131. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js.map +1 -1
  132. package/dist/editor/page-editor-chrome/overlay/placeholderZones.js +23 -1
  133. package/dist/editor/page-editor-chrome/overlay/placeholderZones.js.map +1 -1
  134. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +5 -3
  135. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +111 -19
  136. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -1
  137. package/dist/editor/page-viewer/EditorForm.d.ts +2 -1
  138. package/dist/editor/page-viewer/EditorForm.js +41 -12
  139. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  140. package/dist/editor/page-viewer/PageViewer.d.ts +2 -0
  141. package/dist/editor/page-viewer/PageViewer.js +11 -2
  142. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  143. package/dist/editor/page-viewer/PageViewerFrame.js +266 -44
  144. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  145. package/dist/editor/page-viewer/bridgeFieldPatch.js +1 -0
  146. package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -1
  147. package/dist/editor/page-viewer/bridgeInlineAiSelection.d.ts +17 -0
  148. package/dist/editor/page-viewer/bridgeInlineAiSelection.js +80 -0
  149. package/dist/editor/page-viewer/bridgeInlineAiSelection.js.map +1 -0
  150. package/dist/editor/page-viewer/bridgeInlineEditCommit.d.ts +18 -0
  151. package/dist/editor/page-viewer/bridgeInlineEditCommit.js +24 -0
  152. package/dist/editor/page-viewer/bridgeInlineEditCommit.js.map +1 -0
  153. package/dist/editor/page-viewer/bridgeInlineEditSession.d.ts +3 -0
  154. package/dist/editor/page-viewer/bridgeInlineEditSession.js +30 -0
  155. package/dist/editor/page-viewer/bridgeInlineEditSession.js.map +1 -1
  156. package/dist/editor/page-viewer/bridgeItemChangeTarget.d.ts +9 -0
  157. package/dist/editor/page-viewer/bridgeItemChangeTarget.js +38 -0
  158. package/dist/editor/page-viewer/bridgeItemChangeTarget.js.map +1 -0
  159. package/dist/editor/page-viewer/bridgePointerDragRelease.d.ts +31 -0
  160. package/dist/editor/page-viewer/bridgePointerDragRelease.js +32 -0
  161. package/dist/editor/page-viewer/bridgePointerDragRelease.js.map +1 -0
  162. package/dist/editor/page-viewer/renderUrl.d.ts +8 -0
  163. package/dist/editor/page-viewer/renderUrl.js +10 -0
  164. package/dist/editor/page-viewer/renderUrl.js.map +1 -1
  165. package/dist/editor/personalization/PersonalizationPanel.js +52 -25
  166. package/dist/editor/personalization/PersonalizationPanel.js.map +1 -1
  167. package/dist/editor/reviews/Comment.js +3 -1
  168. package/dist/editor/reviews/Comment.js.map +1 -1
  169. package/dist/editor/reviews/CommentDisplayPopover.js +2 -0
  170. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  171. package/dist/editor/reviews/Comments.js +16 -3
  172. package/dist/editor/reviews/Comments.js.map +1 -1
  173. package/dist/editor/reviews/SuggestedEdit.js +25 -2
  174. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  175. package/dist/editor/reviews/SuggestionCommentThread.js +2 -0
  176. package/dist/editor/reviews/SuggestionCommentThread.js.map +1 -1
  177. package/dist/editor/reviews/applySuggestedEditActions.js +105 -25
  178. package/dist/editor/reviews/applySuggestedEditActions.js.map +1 -1
  179. package/dist/editor/reviews/commentState.d.ts +4 -0
  180. package/dist/editor/reviews/commentState.js +15 -0
  181. package/dist/editor/reviews/commentState.js.map +1 -0
  182. package/dist/editor/reviews/feedbackOrdering.d.ts +1 -0
  183. package/dist/editor/reviews/feedbackOrdering.js +6 -0
  184. package/dist/editor/reviews/feedbackOrdering.js.map +1 -1
  185. package/dist/editor/reviews/suggestionContentChange.d.ts +7 -0
  186. package/dist/editor/reviews/suggestionContentChange.js +10 -0
  187. package/dist/editor/reviews/suggestionContentChange.js.map +1 -1
  188. package/dist/editor/reviews/suggestionPatch.js +6 -0
  189. package/dist/editor/reviews/suggestionPatch.js.map +1 -1
  190. package/dist/editor/services/agentService.d.ts +6 -0
  191. package/dist/editor/services/agentService.js.map +1 -1
  192. package/dist/editor/services/personalizationService.js +30 -6
  193. package/dist/editor/services/personalizationService.js.map +1 -1
  194. package/dist/editor/services/suggestedEditsService.d.ts +1 -0
  195. package/dist/editor/services/suggestedEditsService.js +3 -0
  196. package/dist/editor/services/suggestedEditsService.js.map +1 -1
  197. package/dist/editor/settings/SettingsView.js +15 -7
  198. package/dist/editor/settings/SettingsView.js.map +1 -1
  199. package/dist/editor/settings/panels/MemoriesPanel.js +3 -2
  200. package/dist/editor/settings/panels/MemoriesPanel.js.map +1 -1
  201. package/dist/editor/settings/panels/ProjectTemplatesPanel.d.ts +7 -0
  202. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +232 -21
  203. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  204. package/dist/editor/settings/panels/projectTemplateDuplication.d.ts +5 -0
  205. package/dist/editor/settings/panels/projectTemplateDuplication.js +57 -0
  206. package/dist/editor/settings/panels/projectTemplateDuplication.js.map +1 -0
  207. package/dist/editor/setup-wizard/steps/CreateSettingsStep.js +9 -4
  208. package/dist/editor/setup-wizard/steps/CreateSettingsStep.js.map +1 -1
  209. package/dist/editor/sidebar/MainContentTree.js +1 -1
  210. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  211. package/dist/editor/ui/PerfectTree.js +2 -2
  212. package/dist/editor/ui/PerfectTree.js.map +1 -1
  213. package/dist/editor/ui/PublishRestrictionsDialog.js +9 -1
  214. package/dist/editor/ui/PublishRestrictionsDialog.js.map +1 -1
  215. package/dist/editor/utils.js +5 -2
  216. package/dist/editor/utils.js.map +1 -1
  217. package/dist/editor/views/EditorSlot.js +5 -1
  218. package/dist/editor/views/EditorSlot.js.map +1 -1
  219. package/dist/editor/views/SingleEditView.d.ts +4 -1
  220. package/dist/editor/views/SingleEditView.js +2 -2
  221. package/dist/editor/views/SingleEditView.js.map +1 -1
  222. package/dist/licensing/LicenseContext.js +7 -0
  223. package/dist/licensing/LicenseContext.js.map +1 -1
  224. package/dist/revision.d.ts +2 -2
  225. package/dist/revision.js +2 -2
  226. package/dist/task-board/TaskBoardWorkspace.js +10 -10
  227. package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
  228. package/dist/task-board/taskExecutionRecords.d.ts +1 -0
  229. package/dist/task-board/taskExecutionRecords.js +29 -0
  230. package/dist/task-board/taskExecutionRecords.js.map +1 -1
  231. package/dist/task-board/types.d.ts +9 -0
  232. package/dist/task-board/views/DependencyGraphView.d.ts +1 -0
  233. package/dist/task-board/views/DependencyGraphView.js +15 -12
  234. package/dist/task-board/views/DependencyGraphView.js.map +1 -1
  235. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChevronLeft, CopyPlus, CornerDownRight, GitBranch, LayoutTemplate, Link2, PanelRightOpen, Plus, RefreshCw, Settings2, Shield, Trash2, X, } from "lucide-react";
2
+ import { ArrowDown, ArrowUp, ChevronLeft, CopyPlus, CornerDownRight, GitBranch, LayoutTemplate, ListChecks, Link2, PanelRightOpen, Plus, RefreshCw, Settings2, Shield, Trash2, X, } from "lucide-react";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState, Suspense, lazy, } from "react";
4
4
  import { toast } from "sonner";
5
5
  import { Badge } from "../../../components/ui/badge";
@@ -32,6 +32,7 @@ import { isTypingEventTarget } from "../../utils/keyboardNavigation";
32
32
  import { useRequiredSettingsBreadcrumbItems, useRequiredSettingsHeaderActions, useRequiredSettingsMainContentRef, } from "../SettingsHeaderActionsContext";
33
33
  import { AgentProfileEditorPanel, } from "./AgentProfileEditorPanel";
34
34
  import { ProjectTemplateAgentPanel } from "./ProjectTemplateAgentPanel";
35
+ import { duplicateProjectTemplate, getDuplicateProjectTemplateName, } from "./projectTemplateDuplication";
35
36
  import { ProjectTemplateSelector } from "./ProjectTemplateSelector";
36
37
  import { ProjectTemplateSelectorList } from "./ProjectTemplateSelectorList";
37
38
  // Query param for deep-linking the selected project template (settings reload).
@@ -90,7 +91,17 @@ function isProjectTemplateItemChange(changes, selectedTemplateId) {
90
91
  function cloneTemplate(template) {
91
92
  return normalizeProjectTemplate(JSON.parse(JSON.stringify(template)));
92
93
  }
93
- function normalizeTaskTemplate(task) {
94
+ function normalizeTaskTemplateTodo(todo, index) {
95
+ return {
96
+ ...todo,
97
+ id: todo.id?.trim() || crypto.randomUUID(),
98
+ title: todo.title ?? "",
99
+ instructions: todo.instructions ?? "",
100
+ agentProfileId: todo.agentProfileId?.trim() || null,
101
+ sortOrder: Number.isFinite(todo.sortOrder) ? todo.sortOrder : index * 100,
102
+ };
103
+ }
104
+ export function normalizeTaskTemplate(task) {
94
105
  const rawAssigneeId = task.assigneeId?.trim() || null;
95
106
  const rawAgentProfileId = task.agentProfileId?.trim() || null;
96
107
  const rawParentTaskId = task.parentTaskId?.trim() || null;
@@ -107,6 +118,8 @@ function normalizeTaskTemplate(task) {
107
118
  : "MultiSelect"
108
119
  : null;
109
120
  const assigneeType = task.assigneeType ?? (rawAgentProfileId ? "Agent" : null);
121
+ const todos = (task.todos ?? []).map(normalizeTaskTemplateTodo);
122
+ const todosManaged = task.todosManaged === true && todos.length > 0;
110
123
  if (assigneeType === "Agent") {
111
124
  const canonicalAgentId = rawAgentProfileId || (isValidGuid(rawAssigneeId) ? rawAssigneeId : null);
112
125
  return {
@@ -119,6 +132,8 @@ function normalizeTaskTemplate(task) {
119
132
  agentProfileId: canonicalAgentId,
120
133
  parentTaskId: rawParentTaskId,
121
134
  dependencyIds,
135
+ todos,
136
+ todosManaged,
122
137
  };
123
138
  }
124
139
  if (assigneeType === "user" || assigneeType === "Role") {
@@ -132,6 +147,8 @@ function normalizeTaskTemplate(task) {
132
147
  agentProfileId: null,
133
148
  parentTaskId: rawParentTaskId,
134
149
  dependencyIds,
150
+ todos,
151
+ todosManaged,
135
152
  };
136
153
  }
137
154
  return {
@@ -144,6 +161,8 @@ function normalizeTaskTemplate(task) {
144
161
  agentProfileId: null,
145
162
  parentTaskId: rawParentTaskId,
146
163
  dependencyIds,
164
+ todos,
165
+ todosManaged,
147
166
  };
148
167
  }
149
168
  function normalizeGraphOrientation(orientation) {
@@ -195,6 +214,17 @@ function createEmptyTaskTemplate(taskCount, title) {
195
214
  sortOrder: taskCount * 100,
196
215
  parentTaskId: null,
197
216
  dependencyIds: [],
217
+ todos: [],
218
+ todosManaged: false,
219
+ };
220
+ }
221
+ function createEmptyTaskTemplateTodo(todoCount) {
222
+ return {
223
+ id: crypto.randomUUID(),
224
+ title: `Todo ${todoCount + 1}`,
225
+ instructions: "",
226
+ agentProfileId: null,
227
+ sortOrder: todoCount * 100,
198
228
  };
199
229
  }
200
230
  function createEmptyGoalDefinition(goalCount, title) {
@@ -211,7 +241,7 @@ function createEmptyGoalDefinition(goalCount, title) {
211
241
  function sortTemplates(templates) {
212
242
  return [...templates].sort((left, right) => left.name.localeCompare(right.name, undefined, { sensitivity: "base" }));
213
243
  }
214
- function buildUpsertTemplateRequest(template) {
244
+ export function buildUpsertTemplateRequest(template) {
215
245
  return {
216
246
  projectTemplateId: template.id,
217
247
  name: template.name.trim(),
@@ -248,6 +278,15 @@ function buildUpsertTemplateRequest(template) {
248
278
  assigneeType: normalizedTask.assigneeType || null,
249
279
  assigneeId: normalizedTask.assigneeId || null,
250
280
  dependencyIds: Array.from(new Set(normalizedTask.dependencyIds || [])),
281
+ todosManaged: normalizedTask.todosManaged === true &&
282
+ (normalizedTask.todos?.length ?? 0) > 0,
283
+ todos: (normalizedTask.todos ?? []).map((todo, todoIndex) => ({
284
+ id: todo.id,
285
+ title: todo.title.trim(),
286
+ instructions: todo.instructions?.trim() || null,
287
+ agentProfileId: todo.agentProfileId || null,
288
+ sortOrder: todoIndex * 100,
289
+ })),
251
290
  };
252
291
  }),
253
292
  goalTemplates: (template.goalTemplates ?? []).map((goal, index) => ({
@@ -261,7 +300,7 @@ function buildUpsertTemplateRequest(template) {
261
300
  })),
262
301
  };
263
302
  }
264
- function mergeTemplateResponseWithRequest(serverTemplate, request) {
303
+ export function mergeTemplateResponseWithRequest(serverTemplate, request) {
265
304
  const requestedTaskById = new Map((request.taskTemplates ?? []).map((task) => [task.id, task]));
266
305
  const requestedGoalById = new Map((request.goalTemplates ?? []).map((goal) => [goal.id, goal]));
267
306
  // Server re-reads goals after BeginEdit/EndEdit; if a just-written goal is
@@ -309,6 +348,9 @@ function mergeTemplateResponseWithRequest(serverTemplate, request) {
309
348
  dependencyIds: serverDeps.length === 0 && requestedDeps.length > 0
310
349
  ? Array.from(new Set(requestedDeps))
311
350
  : serverDeps,
351
+ todos: requested?.todos ?? task.todos ?? [],
352
+ todosManaged: (requested?.todos?.length ?? task.todos?.length ?? 0) > 0 &&
353
+ (requested?.todosManaged ?? task.todosManaged) === true,
312
354
  };
313
355
  }),
314
356
  });
@@ -372,19 +414,15 @@ function isValidGuid(value) {
372
414
  return (!!value &&
373
415
  /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value));
374
416
  }
375
- function readProjectTemplateIdFromUrl(searchParams) {
376
- const raw = searchParams.get(SELECTED_PROJECT_TEMPLATE_QUERY_PARAM) ||
377
- (typeof window !== "undefined"
378
- ? new URLSearchParams(window.location.search).get(SELECTED_PROJECT_TEMPLATE_QUERY_PARAM)
379
- : null);
380
- return raw && isValidGuid(raw) ? raw : null;
381
- }
382
- function readProjectTemplateIdFromWindow() {
417
+ export function readProjectTemplateIdFromUrl(searchParams) {
383
418
  const raw = typeof window !== "undefined"
384
419
  ? new URLSearchParams(window.location.search).get(SELECTED_PROJECT_TEMPLATE_QUERY_PARAM)
385
- : null;
420
+ : searchParams.get(SELECTED_PROJECT_TEMPLATE_QUERY_PARAM);
386
421
  return raw && isValidGuid(raw) ? raw : null;
387
422
  }
423
+ function readProjectTemplateIdFromWindow() {
424
+ return readProjectTemplateIdFromUrl(new URLSearchParams());
425
+ }
388
426
  function getSelectedTaskIdForTemplate(template, preferredTaskId) {
389
427
  const taskTemplates = template?.taskTemplates ?? [];
390
428
  if (!taskTemplates.length) {
@@ -533,6 +571,7 @@ export function ProjectTemplatesPanel() {
533
571
  const [templates, setTemplates] = useState([]);
534
572
  const [error, setError] = useState(null);
535
573
  const [selectedTemplateId, setSelectedTemplateId] = useState(null);
574
+ const [persistedTemplateId, setPersistedTemplateId] = useState(null);
536
575
  const [draftTemplate, setDraftTemplate] = useState(null);
537
576
  const [selectedTaskId, setSelectedTaskId] = useState(null);
538
577
  const [saving, setSaving] = useState(false);
@@ -837,6 +876,7 @@ export function ProjectTemplatesPanel() {
837
876
  selectedTemplateIdRef.current = template?.id ?? null;
838
877
  draftTemplateRef.current = clonedTemplate;
839
878
  setSelectedTemplateId(template?.id ?? null);
879
+ setPersistedTemplateId(template?.id ?? null);
840
880
  setDraftTemplate(clonedTemplate);
841
881
  setEditingAgentProfile(null);
842
882
  setSelectedTaskId(getSelectedTaskIdForTemplate(clonedTemplate, preferredTaskId));
@@ -963,6 +1003,16 @@ export function ProjectTemplatesPanel() {
963
1003
  }
964
1004
  return byId;
965
1005
  }, [aiProfiles]);
1006
+ const taskTodoProfileOptions = useMemo(() => [
1007
+ { value: "__task_agent__", label: "Use task agent profile" },
1008
+ ...aiProfiles
1009
+ .filter((profile) => !!profile.id)
1010
+ .map((profile) => ({
1011
+ value: profile.id,
1012
+ label: profile.displayTitle || profile.name || profile.id,
1013
+ }))
1014
+ .sort((left, right) => left.label.localeCompare(right.label)),
1015
+ ], [aiProfiles]);
966
1016
  const selectedTask = useMemo(() => draftTemplate?.taskTemplates.find((task) => task.id === selectedTaskId) ??
967
1017
  null, [draftTemplate, selectedTaskId]);
968
1018
  const templateAgentInitialMetadata = useMemo(() => draftTemplate
@@ -1062,6 +1112,16 @@ export function ProjectTemplatesPanel() {
1062
1112
  setTemplateAgentError(null);
1063
1113
  return;
1064
1114
  }
1115
+ // New and duplicated templates are shown optimistically before their first
1116
+ // save has reached Sitecore. Resolving the assistant during that window
1117
+ // races the upsert and produces a transient "Project template not found"
1118
+ // response. Wait until persistTemplate confirms this ID exists remotely.
1119
+ if (persistedTemplateId !== selectedTemplateId) {
1120
+ setTemplateAgentId(null);
1121
+ setTemplateAgentLoading(true);
1122
+ setTemplateAgentError(null);
1123
+ return;
1124
+ }
1065
1125
  setTemplateAgentId(null);
1066
1126
  setTemplateAgentLoading(true);
1067
1127
  setTemplateAgentError(null);
@@ -1085,7 +1145,12 @@ export function ProjectTemplatesPanel() {
1085
1145
  ? loadError.message
1086
1146
  : "Failed to load project template assistant");
1087
1147
  });
1088
- }, [draftTemplate?.id, selectedTemplateId, syncProjectTemplateAgentContext]);
1148
+ }, [
1149
+ draftTemplate?.id,
1150
+ persistedTemplateId,
1151
+ selectedTemplateId,
1152
+ syncProjectTemplateAgentContext,
1153
+ ]);
1089
1154
  useEffect(() => {
1090
1155
  if (!templateAgentId || templateAgentLoading)
1091
1156
  return;
@@ -1125,6 +1190,59 @@ export function ProjectTemplatesPanel() {
1125
1190
  taskTemplates: currentTemplate.taskTemplates.map((task) => task.id === selectedTaskId ? updater({ ...task }) : task),
1126
1191
  }));
1127
1192
  }, [applyDraftChange, selectedTaskId]);
1193
+ const handleAddTaskTodo = useCallback(() => {
1194
+ updateSelectedTask((currentTask) => ({
1195
+ ...currentTask,
1196
+ todos: [
1197
+ ...(currentTask.todos ?? []),
1198
+ createEmptyTaskTemplateTodo(currentTask.todos?.length ?? 0),
1199
+ ],
1200
+ }));
1201
+ }, [updateSelectedTask]);
1202
+ const handleUpdateTaskTodo = useCallback((todoId, updater) => {
1203
+ updateSelectedTask((currentTask) => ({
1204
+ ...currentTask,
1205
+ todos: (currentTask.todos ?? []).map((todo) => todo.id === todoId ? updater({ ...todo }) : todo),
1206
+ }));
1207
+ }, [updateSelectedTask]);
1208
+ const handleRemoveTaskTodo = useCallback((todoId) => {
1209
+ updateSelectedTask((currentTask) => {
1210
+ const todos = (currentTask.todos ?? [])
1211
+ .filter((todo) => todo.id !== todoId)
1212
+ .map((todo, index) => ({ ...todo, sortOrder: index * 100 }));
1213
+ return {
1214
+ ...currentTask,
1215
+ todos,
1216
+ todosManaged: todos.length > 0 && currentTask.todosManaged === true,
1217
+ };
1218
+ });
1219
+ }, [updateSelectedTask]);
1220
+ const handleMoveTaskTodo = useCallback((todoId, direction) => {
1221
+ updateSelectedTask((currentTask) => {
1222
+ const todos = [...(currentTask.todos ?? [])];
1223
+ const currentIndex = todos.findIndex((todo) => todo.id === todoId);
1224
+ const targetIndex = currentIndex + direction;
1225
+ if (currentIndex < 0 ||
1226
+ targetIndex < 0 ||
1227
+ targetIndex >= todos.length) {
1228
+ return currentTask;
1229
+ }
1230
+ const currentTodo = todos[currentIndex];
1231
+ const targetTodo = todos[targetIndex];
1232
+ if (currentTodo === undefined || targetTodo === undefined) {
1233
+ return currentTask;
1234
+ }
1235
+ todos[currentIndex] = targetTodo;
1236
+ todos[targetIndex] = currentTodo;
1237
+ return {
1238
+ ...currentTask,
1239
+ todos: todos.map((todo, index) => ({
1240
+ ...todo,
1241
+ sortOrder: index * 100,
1242
+ })),
1243
+ };
1244
+ });
1245
+ }, [updateSelectedTask]);
1128
1246
  const updateSelectedGoal = useCallback((updater) => {
1129
1247
  if (!selectedGoalId)
1130
1248
  return;
@@ -1248,6 +1366,9 @@ export function ProjectTemplatesPanel() {
1248
1366
  }
1249
1367
  const savedTemplate = mergeTemplateResponseWithRequest(result.data, request);
1250
1368
  lastPersistedSignatureRef.current = signature;
1369
+ if (selectedTemplateIdRef.current === savedTemplate.id) {
1370
+ setPersistedTemplateId(savedTemplate.id);
1371
+ }
1251
1372
  setTemplates((existingTemplates) => sortTemplates([
1252
1373
  ...existingTemplates.filter((template) => template.id !== savedTemplate.id),
1253
1374
  savedTemplate,
@@ -1341,13 +1462,10 @@ export function ProjectTemplatesPanel() {
1341
1462
  }
1342
1463
  setEditingAgentProfile(null);
1343
1464
  selectTemplate(null);
1344
- editContext?.updateUrl({
1345
- [SELECTED_PROJECT_TEMPLATE_QUERY_PARAM]: undefined,
1346
- });
1347
1465
  if (isMobile) {
1348
1466
  setMobileView("detail");
1349
1467
  }
1350
- }, [editContext, flushPendingSave, isMobile, selectTemplate]);
1468
+ }, [flushPendingSave, isMobile, selectTemplate]);
1351
1469
  useEffect(() => {
1352
1470
  if (autosaveTimeoutRef.current !== null) {
1353
1471
  window.clearTimeout(autosaveTimeoutRef.current);
@@ -1517,6 +1635,7 @@ export function ProjectTemplatesPanel() {
1517
1635
  latestDraftSignatureRef.current = signature;
1518
1636
  setTemplates((current) => sortTemplates([nextTemplate, ...current]));
1519
1637
  setSelectedTemplateId(nextTemplate.id);
1638
+ setPersistedTemplateId(null);
1520
1639
  setDraftTemplate(cloneTemplate(nextTemplate));
1521
1640
  setSelectedTaskId(null);
1522
1641
  setSelectedGoalId(null);
@@ -1532,6 +1651,80 @@ export function ProjectTemplatesPanel() {
1532
1651
  }
1533
1652
  await persistTemplate(request, signature, { showErrorToast: true });
1534
1653
  }, [editContext, flushPendingSave, isMobile, persistTemplate, templates]);
1654
+ const handleDuplicateTemplate = useCallback(async () => {
1655
+ const canContinue = await flushPendingSave({ showErrorToast: true });
1656
+ if (!canContinue) {
1657
+ return;
1658
+ }
1659
+ const sourceTemplate = draftTemplateRef.current ?? draftTemplate;
1660
+ if (!sourceTemplate) {
1661
+ return;
1662
+ }
1663
+ const suggestedName = getDuplicateProjectTemplateName(sourceTemplate.name, templates.map((template) => template.name));
1664
+ let templateName = null;
1665
+ if (editContext) {
1666
+ templateName = await editContext.openDialog(ItemNameDialog, {
1667
+ name: suggestedName,
1668
+ title: "Duplicate Project Template",
1669
+ message: "Enter a name for the duplicated template:",
1670
+ headerIcon: _jsx(CopyPlus, { strokeWidth: 1 }),
1671
+ });
1672
+ }
1673
+ else {
1674
+ templateName = window.prompt("Enter a name for the duplicated template:", suggestedName);
1675
+ }
1676
+ if (templateName == null) {
1677
+ return;
1678
+ }
1679
+ const trimmedName = templateName.trim();
1680
+ if (!trimmedName) {
1681
+ return;
1682
+ }
1683
+ const nameLower = trimmedName.toLowerCase();
1684
+ if (templates.some((t) => t.name.trim().toLowerCase() === nameLower)) {
1685
+ toast.error("A project template with this name already exists");
1686
+ return;
1687
+ }
1688
+ const nextTemplate = duplicateProjectTemplate(sourceTemplate, trimmedName);
1689
+ const request = buildUpsertTemplateRequest(nextTemplate);
1690
+ const signature = JSON.stringify(request);
1691
+ if (autosaveTimeoutRef.current !== null) {
1692
+ window.clearTimeout(autosaveTimeoutRef.current);
1693
+ autosaveTimeoutRef.current = null;
1694
+ }
1695
+ selectedTemplateIdRef.current = nextTemplate.id;
1696
+ draftTemplateRef.current = nextTemplate;
1697
+ latestDraftSignatureRef.current = signature;
1698
+ setTemplates((current) => sortTemplates([nextTemplate, ...current]));
1699
+ setSelectedTemplateId(nextTemplate.id);
1700
+ setPersistedTemplateId(null);
1701
+ setDraftTemplate(cloneTemplate(nextTemplate));
1702
+ setSelectedTaskId(getSelectedTaskIdForTemplate(nextTemplate, null));
1703
+ setSelectedGoalId(null);
1704
+ setIsDirty(true);
1705
+ setSaveError(null);
1706
+ lastPersistedSignatureRef.current = null;
1707
+ latestDraftSignatureRef.current = null;
1708
+ editContext?.updateUrl({
1709
+ [SELECTED_PROJECT_TEMPLATE_QUERY_PARAM]: nextTemplate.id,
1710
+ });
1711
+ if (isMobile) {
1712
+ setMobileView("detail");
1713
+ }
1714
+ const didPersist = await persistTemplate(request, signature, {
1715
+ showErrorToast: true,
1716
+ });
1717
+ if (didPersist) {
1718
+ toast.success("Project template duplicated");
1719
+ }
1720
+ }, [
1721
+ draftTemplate,
1722
+ editContext,
1723
+ flushPendingSave,
1724
+ isMobile,
1725
+ persistTemplate,
1726
+ templates,
1727
+ ]);
1535
1728
  const handleDeleteTemplate = useCallback((templateToDelete) => {
1536
1729
  const template = templateToDelete ?? draftTemplate;
1537
1730
  if (!template || template.isSystem)
@@ -2112,17 +2305,19 @@ export function ProjectTemplatesPanel() {
2112
2305
  : 0;
2113
2306
  return (_jsxs("div", { className: "mt-2 flex items-center gap-2", style: agentPanelHeaderOffset > 0
2114
2307
  ? { marginRight: agentPanelHeaderOffset }
2115
- : undefined, children: [headerStatusBadge, showCreate ? (_jsxs(Button, { size: "sm", onClick: () => void handleCreateTemplate(), "data-testid": "project-template-create-button", children: [_jsx(Plus, { className: "h-4 w-4", strokeWidth: 1 }), "Create template"] })) : null, showDelete ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => handleDeleteTemplate(), disabled: deleting, "data-testid": "project-template-delete-button", children: [_jsx(DeleteIcon, { size: "lg" }), deleting ? "Deleting..." : "Delete"] })) : null] }));
2308
+ : undefined, children: [headerStatusBadge, showCreate ? (_jsxs(Button, { size: "sm", onClick: () => void handleCreateTemplate(), "data-testid": "project-template-create-button", children: [_jsx(Plus, { className: "h-4 w-4", strokeWidth: 1 }), "Create template"] })) : null, draftTemplate ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void handleDuplicateTemplate(), disabled: saving || deleting, "data-testid": "project-template-duplicate-button", children: [_jsx(CopyPlus, { className: "h-4 w-4", strokeWidth: 1 }), "Duplicate"] })) : null, showDelete ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => handleDeleteTemplate(), disabled: deleting, "data-testid": "project-template-delete-button", children: [_jsx(DeleteIcon, { size: "lg" }), deleting ? "Deleting..." : "Delete"] })) : null] }));
2116
2309
  }, [
2117
2310
  agentPanelBounds,
2118
2311
  deleting,
2119
2312
  draftTemplate,
2120
2313
  handleCreateTemplate,
2121
2314
  handleDeleteTemplate,
2315
+ handleDuplicateTemplate,
2122
2316
  headerStatusBadge,
2123
2317
  isAgentPanelCollapsed,
2124
2318
  isTaskEditorMaximized,
2125
2319
  isMobile,
2320
+ saving,
2126
2321
  ]);
2127
2322
  useEffect(() => {
2128
2323
  if (!setSettingsBreadcrumbItems)
@@ -2188,7 +2383,23 @@ export function ProjectTemplatesPanel() {
2188
2383
  agentProfileId: next?.assigneeType === "Agent"
2189
2384
  ? next.assigneeId
2190
2385
  : null,
2191
- })), placeholder: "Assign user or agent", buttonClassName: "h-8 min-h-0 w-full justify-between rounded-md bg-white text-xs", buttonTestId: "project-template-task-assignee-picker" }) }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-8 shrink-0 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 }), "Edit Profile"] })) : null] }), profilesError ? (_jsx("div", { className: "text-feedback-orange text-xs", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "border-border-default bg-neutral-grey-5 rounded-lg border 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: "text-neutral-grey-100 flex items-center gap-2 text-xs font-medium", children: [_jsx(Shield, { className: "text-neutral-grey-50 h-4 w-4", strokeWidth: 1 }), "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 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-border-default text-neutral-grey-100 border-b px-3 py-2 text-xs font-medium", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length === 0 ? (_jsx("div", { className: "text-neutral-grey-50 px-2 py-3 text-center text-xs", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "hover:bg-neutral-grey-5 flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "text-neutral-grey-50 h-3.5 w-3.5 shrink-0", strokeWidth: 1 }), _jsx("span", { className: "text-neutral-grey-100 min-w-0 truncate font-medium", children: task.title || "Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "text-neutral-grey-50 mb-2 text-[11px] leading-snug", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-neutral-grey-50 text-xs", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
2386
+ })), placeholder: "Assign user or agent", buttonClassName: "h-8 min-h-0 w-full justify-between rounded-md bg-white text-xs", buttonTestId: "project-template-task-assignee-picker" }) }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-8 shrink-0 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 }), "Edit Profile"] })) : null] }), profilesError ? (_jsx("div", { className: "text-feedback-orange text-xs", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "border-border-default bg-neutral-grey-5 rounded-lg border p-3", "data-testid": "project-template-todos-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "text-neutral-grey-100 flex items-center gap-2 text-xs font-medium", children: [_jsx(ListChecks, { className: "text-neutral-grey-50 h-4 w-4", strokeWidth: 1 }), "Todos"] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-1.5", title: (selectedTask.todos?.length ?? 0) > 0
2387
+ ? "Deliver todo items to the agent one at a time"
2388
+ : "Add at least one todo to enable managed execution", children: [_jsx(Switch, { id: "project-template-todos-managed", checked: selectedTask.todosManaged === true, disabled: (selectedTask.todos?.length ?? 0) === 0, onCheckedChange: (checked) => updateSelectedTask((currentTask) => ({
2389
+ ...currentTask,
2390
+ todosManaged: checked &&
2391
+ (currentTask.todos?.length ?? 0) > 0,
2392
+ })), "aria-label": "Managed todo execution", "data-testid": "project-template-todos-managed-toggle" }), _jsx(Label, { htmlFor: "project-template-todos-managed", className: "text-neutral-grey-100 text-xs", children: "Managed" })] }), _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", onClick: handleAddTaskTodo, "aria-label": "Add todo", "data-testid": "project-template-add-todo-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1 }) })] })] }), _jsx("p", { className: "text-neutral-grey-50 mb-2 text-[11px] leading-snug", children: "These task-specific todos override predefined todos from the assigned agent profile. Managed lists are delivered one item at a time after a brief orientation turn." }), (selectedTask.todos ?? []).length === 0 ? (_jsx("div", { className: "text-neutral-grey-50 text-xs", children: "No todos yet. The agent profile defaults will be used." })) : (_jsx("div", { className: "space-y-2", children: (selectedTask.todos ?? []).map((todo, todoIndex) => (_jsxs("div", { className: "border-border-default space-y-2 rounded-md border bg-white p-2", "data-testid": `project-template-todo-row-${todo.id}`, children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { className: "h-8 min-w-0 flex-1 text-xs md:text-xs", value: todo.title, onChange: (event) => handleUpdateTaskTodo(todo.id, (current) => ({
2393
+ ...current,
2394
+ title: event.target.value,
2395
+ })), placeholder: "Todo title", "aria-label": `Todo ${todoIndex + 1} title`, "data-testid": "project-template-todo-title-input" }), _jsx(Button, { type: "button", size: "icon", variant: "ghost", className: "h-7 w-7 shrink-0", disabled: todoIndex === 0, onClick: () => handleMoveTaskTodo(todo.id, -1), "aria-label": `Move ${todo.title || "todo"} up`, "data-testid": "project-template-move-todo-up", children: _jsx(ArrowUp, { className: "h-3.5 w-3.5", strokeWidth: 1 }) }), _jsx(Button, { type: "button", size: "icon", variant: "ghost", className: "h-7 w-7 shrink-0", disabled: todoIndex ===
2396
+ (selectedTask.todos?.length ?? 0) - 1, onClick: () => handleMoveTaskTodo(todo.id, 1), "aria-label": `Move ${todo.title || "todo"} down`, "data-testid": "project-template-move-todo-down", children: _jsx(ArrowDown, { className: "h-3.5 w-3.5", strokeWidth: 1 }) }), _jsx(Button, { type: "button", size: "icon", variant: "ghost", className: "text-feedback-red h-7 w-7 shrink-0", onClick: () => handleRemoveTaskTodo(todo.id), "aria-label": `Remove ${todo.title || "todo"}`, "data-testid": "project-template-remove-todo-button", children: _jsx(Trash2, { className: "h-3.5 w-3.5", strokeWidth: 1 }) })] }), _jsx(Textarea, { className: "text-xs", rows: 2, value: todo.instructions ?? "", onChange: (event) => handleUpdateTaskTodo(todo.id, (current) => ({
2397
+ ...current,
2398
+ instructions: event.target.value,
2399
+ })), placeholder: "Instructions for completing this todo", "aria-label": `Todo ${todoIndex + 1} instructions`, "data-testid": "project-template-todo-instructions-input" }), _jsx(Select, { value: todo.agentProfileId ?? "__task_agent__", onValueChange: (value) => handleUpdateTaskTodo(todo.id, (current) => ({
2400
+ ...current,
2401
+ agentProfileId: value === "__task_agent__" ? null : value,
2402
+ })), options: taskTodoProfileOptions, searchable: true, placeholder: "Use task agent profile", "data-testid": "project-template-todo-agent-profile-select" })] }, todo.id))) }))] }), _jsxs("div", { className: "border-border-default bg-neutral-grey-5 rounded-lg border 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: "text-neutral-grey-100 flex items-center gap-2 text-xs font-medium", children: [_jsx(Shield, { className: "text-neutral-grey-50 h-4 w-4", strokeWidth: 1 }), "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 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-border-default text-neutral-grey-100 border-b px-3 py-2 text-xs font-medium", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length === 0 ? (_jsx("div", { className: "text-neutral-grey-50 px-2 py-3 text-center text-xs", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "hover:bg-neutral-grey-5 flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "text-neutral-grey-50 h-3.5 w-3.5 shrink-0", strokeWidth: 1 }), _jsx("span", { className: "text-neutral-grey-100 min-w-0 truncate font-medium", children: task.title || "Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "text-neutral-grey-50 mb-2 text-[11px] leading-snug", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-neutral-grey-50 text-xs", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
2192
2403
  ? "Add more task templates to create dependencies."
2193
2404
  : "No dependencies yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: dependencyTasks.map((task) => (_jsxs("li", { tabIndex: 0, className: cn("flex items-center justify-between gap-2 rounded-md border bg-white px-3 py-2 text-xs transition-colors outline-none", selectedDependencyId === task.id
2194
2405
  ? "border-feedback-orange bg-feedback-orange-light ring-feedback-orange ring-1"
@@ -2228,7 +2439,7 @@ export function ProjectTemplatesPanel() {
2228
2439
  : []),
2229
2440
  ] }) }));
2230
2441
  };
2231
- const detailContent = (_jsxs("div", { className: "bg-neutral-grey-5/40 flex h-full flex-col", "data-testid": "project-template-detail-pane", children: [isMobile && draftTemplate && !draftTemplate.isSystem ? (_jsxs("div", { className: "border-border-default flex min-w-0 items-center gap-3 border-b bg-white px-5 py-2", "data-testid": "project-template-detail-pane-header", children: [_jsx("div", { className: "min-w-0 flex-1", children: templateSelector }), _jsx("div", { className: "flex shrink-0 items-center gap-1.5 self-center", children: _jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0", onClick: () => handleDeleteTemplate(), disabled: deleting, "aria-label": deleting ? "Deleting template" : "Delete template", title: deleting ? "Deleting…" : "Delete template", "data-testid": "project-template-delete-button", children: deleting ? (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin", strokeWidth: 1 })) : (_jsx(DeleteIcon, { size: "lg" })) }) })] })) : null, draftTemplate ? ((() => {
2442
+ const detailContent = (_jsxs("div", { className: "bg-neutral-grey-5/40 flex h-full flex-col", "data-testid": "project-template-detail-pane", children: [isMobile && draftTemplate ? (_jsxs("div", { className: "border-border-default flex min-w-0 items-center gap-3 border-b bg-white px-5 py-2", "data-testid": "project-template-detail-pane-header", children: [_jsx("div", { className: "min-w-0 flex-1", children: templateSelector }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 self-center", children: [_jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0", onClick: () => void handleDuplicateTemplate(), disabled: saving || deleting, "aria-label": "Duplicate template", title: "Duplicate template", "data-testid": "project-template-duplicate-button", children: _jsx(CopyPlus, { className: "h-4 w-4", strokeWidth: 1 }) }), !draftTemplate.isSystem ? (_jsx(Button, { variant: "outline", size: "icon", className: "h-9 w-9 shrink-0", onClick: () => handleDeleteTemplate(), disabled: deleting, "aria-label": deleting ? "Deleting template" : "Delete template", title: deleting ? "Deleting…" : "Delete template", "data-testid": "project-template-delete-button", children: deleting ? (_jsx(RefreshCw, { className: "h-4 w-4 animate-spin", strokeWidth: 1 })) : (_jsx(DeleteIcon, { size: "lg" })) })) : null] })] })) : null, draftTemplate ? ((() => {
2232
2443
  const detailTabs = [
2233
2444
  {
2234
2445
  id: "project-template-tab-basic-settings",