@promptbook/cli 0.114.0-13 → 0.114.0-18

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 (231) hide show
  1. package/apps/agents-server/next.config.ts +6 -0
  2. package/apps/agents-server/src/app/admin/_components/AdminFilterFields.tsx +84 -0
  3. package/apps/agents-server/src/app/admin/_components/AdminMetricCard.tsx +36 -0
  4. package/apps/agents-server/src/app/admin/_components/AdminTaskManagerTabs.tsx +97 -0
  5. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerClient.tsx +103 -0
  6. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerEditDialog.tsx +232 -0
  7. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerFiltersCard.tsx +195 -0
  8. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerRow.tsx +189 -0
  9. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerSummaryMetrics.tsx +60 -0
  10. package/apps/agents-server/src/app/admin/planned-messages/PlannedMessageManagerTableCard.tsx +147 -0
  11. package/apps/agents-server/src/app/admin/planned-messages/page.tsx +17 -0
  12. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageEditForm.ts +186 -0
  13. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageManagerDialogs.ts +46 -0
  14. package/apps/agents-server/src/app/admin/planned-messages/plannedMessageManagerPresentation.tsx +188 -0
  15. package/apps/agents-server/src/app/admin/planned-messages/resolvePlannedMessageSortValue.ts +115 -0
  16. package/apps/agents-server/src/app/admin/planned-messages/usePlannedMessageManagerData.ts +116 -0
  17. package/apps/agents-server/src/app/admin/planned-messages/usePlannedMessageManagerState.ts +289 -0
  18. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +7 -2
  19. package/apps/agents-server/src/app/admin/task-manager/TaskManagerFiltersCard.tsx +7 -73
  20. package/apps/agents-server/src/app/admin/task-manager/TaskManagerSummaryMetrics.tsx +5 -30
  21. package/apps/agents-server/src/app/admin/task-manager/[taskId]/TaskManagerTaskDetailClient.tsx +1 -1
  22. package/apps/agents-server/src/app/agents/[agentName]/api/book/reference-diagnostics/route.ts +30 -7
  23. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +1 -0
  24. package/apps/agents-server/src/app/agents/[agentName]/api/book/test.http +3 -3
  25. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorMissingReferences.tsx +106 -12
  26. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +3 -0
  27. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorDiagnostics.ts +12 -0
  28. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorWrapper.tsx +2 -0
  29. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatPlannedMessages.tsx +46 -3
  30. package/apps/agents-server/src/app/api/admin/default-agents/reinstate/route.ts +5 -1
  31. package/apps/agents-server/src/app/api/admin/planned-messages/[timeoutId]/route.ts +80 -0
  32. package/apps/agents-server/src/app/api/admin/planned-messages/route.ts +19 -0
  33. package/apps/agents-server/src/app/api/internal/agent-goal-chat-planned-messages/route.ts +30 -2
  34. package/apps/agents-server/src/components/DefaultAgents/ReinstateBundledAgentsButton.tsx +7 -0
  35. package/apps/agents-server/src/components/DefaultAgents/coreAgentsAdminRoute.ts +6 -0
  36. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +7 -0
  37. package/apps/agents-server/src/components/Homepage/DefaultAgentsStatusNotice.tsx +1 -7
  38. package/apps/agents-server/src/database/migrations/2026-08-1800-user-chat-timeout-schedule.sql +11 -0
  39. package/apps/agents-server/src/instrumentation-node.ts +18 -0
  40. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +4 -0
  41. package/apps/agents-server/src/languages/translations/czech.yaml +4 -0
  42. package/apps/agents-server/src/languages/translations/english.yaml +4 -0
  43. package/apps/agents-server/src/tools/agentGoalChatTimeoutToolFunctions.ts +62 -15
  44. package/apps/agents-server/src/tools/agentGoalChatTimeoutTools.ts +63 -9
  45. package/apps/agents-server/src/utils/adminEntityLookups/loadAgentNamesByPermanentId.ts +45 -0
  46. package/apps/agents-server/src/utils/adminEntityLookups/loadUsernamesByUserId.ts +36 -0
  47. package/apps/agents-server/src/utils/adminEntityLookups.ts +2 -0
  48. package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatPlannedMessageActions.ts +299 -52
  49. package/apps/agents-server/src/utils/agentGoalChat/createAgentGoalChatNoteContent.ts +45 -17
  50. package/apps/agents-server/src/utils/agentGoalChat.ts +1 -0
  51. package/apps/agents-server/src/utils/agentProjects/agentProjectIdentity.ts +46 -0
  52. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeAutostartScheduler.ts +162 -0
  53. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDesiredState.ts +252 -0
  54. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDisplay.ts +6 -3
  55. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeDomains.ts +2 -44
  56. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimePaths.ts +25 -0
  57. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimePm2.ts +4 -3
  58. package/apps/agents-server/src/utils/agentProjects/agentProjectRuntimeRegistry.ts +77 -24
  59. package/apps/agents-server/src/utils/agentProjects/agentProjectStateMutationQueue.ts +58 -0
  60. package/apps/agents-server/src/utils/agentProjects/listAllLocalAgentProjectIdentities.ts +48 -0
  61. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectRuntimeEnvironmentFlag.ts +23 -0
  62. package/apps/agents-server/src/utils/agentProjects/resolveAgentProjectRuntimeStatus.ts +73 -0
  63. package/apps/agents-server/src/utils/agentProjects/startDefaultAgentProjectRuntimes.ts +110 -0
  64. package/apps/agents-server/src/utils/agentReferenceResolver/MissingCoreAgentRecovery.ts +19 -0
  65. package/apps/agents-server/src/utils/agentReferenceResolver/bookScopedAgentReferences.ts +7 -0
  66. package/apps/agents-server/src/utils/agentReferenceResolver/createAgentInheritanceDiagnostics.ts +457 -0
  67. package/apps/agents-server/src/utils/agentReferenceResolver/createUnresolvedAgentReferenceDiagnostics.ts +27 -0
  68. package/apps/agents-server/src/utils/agentReferenceResolver/resolveCoreAgentAwareMissingReferences.ts +156 -0
  69. package/apps/agents-server/src/utils/chatMessageChips/createAnsweredMessageChipToolCalls.ts +15 -3
  70. package/apps/agents-server/src/utils/chatMessageChips/createPlannedMessageChipToolCalls.ts +8 -3
  71. package/apps/agents-server/src/utils/chatMessageChips/createTouchedExternalSourceChipToolCalls.ts +39 -0
  72. package/apps/agents-server/src/utils/chatMessageChips/createTouchedProjectChipResult.ts +45 -0
  73. package/apps/agents-server/src/utils/chatMessageChips/createTouchedProjectChipToolCalls.ts +58 -24
  74. package/apps/agents-server/src/utils/cronExpression/CronExpression.ts +63 -0
  75. package/apps/agents-server/src/utils/cronExpression/normalizeCronExpression.ts +34 -0
  76. package/apps/agents-server/src/utils/cronExpression/normalizeCronExpressionWhitespace.ts +11 -0
  77. package/apps/agents-server/src/utils/cronExpression/parseCronExpression.ts +41 -0
  78. package/apps/agents-server/src/utils/cronExpression/parseCronExpressionField.ts +140 -0
  79. package/apps/agents-server/src/utils/cronExpression/resolveNextCronRun.ts +100 -0
  80. package/apps/agents-server/src/utils/cronExpression/throwInvalidCronExpressionField.ts +20 -0
  81. package/apps/agents-server/src/utils/cronExpression.ts +10 -0
  82. package/apps/agents-server/src/utils/explicitFromCommitment.ts +130 -0
  83. package/apps/agents-server/src/utils/getAdminChatTasksResponse/getAdminChatTasks/loadAdminChatTaskFallbackData.ts +3 -77
  84. package/apps/agents-server/src/utils/localChatRunner/applyLocalAgentPlannedMessageCommands.ts +53 -4
  85. package/apps/agents-server/src/utils/localChatRunner/prepareLocalAgentPlannedMessagesSidecar.ts +15 -7
  86. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +8 -0
  87. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentBooks.ts +5 -11
  88. package/apps/agents-server/src/utils/manGoOnboarding/manGoOnboardingAgentRuntime.ts +2 -2
  89. package/apps/agents-server/src/utils/managementApi/managementApiSchemas.ts +2 -2
  90. package/apps/agents-server/src/utils/plannedMessageManager/cancelManagedPlannedMessage.ts +35 -0
  91. package/apps/agents-server/src/utils/plannedMessageManager/collectPlannedMessageAgentOptions.ts +56 -0
  92. package/apps/agents-server/src/utils/plannedMessageManager/createPlannedMessageManagerCounters.ts +104 -0
  93. package/apps/agents-server/src/utils/plannedMessageManager/filterPlannedMessages.ts +191 -0
  94. package/apps/agents-server/src/utils/plannedMessageManager/getPlannedMessageManagerResponse.ts +55 -0
  95. package/apps/agents-server/src/utils/plannedMessageManager/isPlannedMessageStillPlanned.ts +18 -0
  96. package/apps/agents-server/src/utils/plannedMessageManager/loadPlannedMessageManagerRecord.ts +32 -0
  97. package/apps/agents-server/src/utils/plannedMessageManager/mapPlannedMessageManagerRecord.ts +73 -0
  98. package/apps/agents-server/src/utils/plannedMessageManager/parsePlannedMessageManagerUpdateRequest.ts +76 -0
  99. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageEndReason.ts +59 -0
  100. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageLifecycle.ts +99 -0
  101. package/apps/agents-server/src/utils/plannedMessageManager/resolvePlannedMessageRecurrenceKind.ts +40 -0
  102. package/apps/agents-server/src/utils/plannedMessageManager/updateManagedPlannedMessage.ts +121 -0
  103. package/apps/agents-server/src/utils/plannedMessagesAdmin.ts +200 -0
  104. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +114 -95
  105. package/apps/agents-server/src/utils/userChatClient.ts +4 -0
  106. package/apps/agents-server/src/utils/userChatTimeout/UserChatTimeoutRecord.ts +37 -1
  107. package/apps/agents-server/src/utils/userChatTimeout/agentScopedTimeoutBulkActions.ts +11 -17
  108. package/apps/agents-server/src/utils/userChatTimeout/createTimeoutWakeUpMessage.ts +37 -13
  109. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/PlannedMessageSchedule.ts +52 -0
  110. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/hasPlannedMessageRecurrence.ts +13 -0
  111. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/isPlannedMessageScheduleFinished.ts +30 -0
  112. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/normalizePlannedMessageScheduleValues.ts +62 -0
  113. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/parsePlannedMessageSchedule.ts +202 -0
  114. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule/resolvePlannedMessageDueAt.ts +121 -0
  115. package/apps/agents-server/src/utils/userChatTimeout/plannedMessageSchedule.ts +12 -0
  116. package/apps/agents-server/src/utils/userChatTimeout/setAgentScopedUserChatTimeoutPausedState.ts +66 -0
  117. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/createUserChatTimeout.ts +76 -5
  118. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/listAllUserChatTimeouts.ts +37 -0
  119. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/mapUserChatTimeoutRow.ts +9 -0
  120. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/repeatFiredUserChatTimeout.ts +19 -9
  121. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/updateAgentScopedUserChatTimeout.ts +39 -6
  122. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore.ts +1 -0
  123. package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutWorker.ts +110 -11
  124. package/apps/agents-server/src/utils/userChatTimeout.ts +16 -0
  125. package/apps/agents-server/src/utils/vpsSelfUpdate/vpsSelfUpdateCron.ts +23 -316
  126. package/apps/agents-server/tests/e2e/support/AgentManagementApi.ts +3 -3
  127. package/esm/index.es.js +951 -102
  128. package/esm/index.es.js.map +1 -1
  129. package/esm/scripts/run-agent-messages/git/commitAgentProjectChanges.d.ts +15 -0
  130. package/esm/scripts/run-agent-messages/git/commitAnsweredMessageProjectChanges.d.ts +24 -0
  131. package/esm/scripts/run-agent-messages/git/ensureAgentProjectGitRepository.d.ts +20 -0
  132. package/esm/scripts/run-agent-messages/git/readAgentProjectCommitChange.d.ts +11 -0
  133. package/esm/scripts/run-agent-messages/git/runAgentProjectGitCommand.d.ts +29 -0
  134. package/esm/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +3 -3
  135. package/esm/scripts/run-agent-messages/messages/listAgentProjectDirectoryNames.d.ts +7 -0
  136. package/esm/scripts/run-agent-messages/messages/resolveAnsweredMessageTouches.d.ts +29 -0
  137. package/esm/scripts/run-codex-prompts/ping/pingCoderHarnessPeriodically.d.ts +22 -0
  138. package/esm/src/book-3.0/AgentMessageRunReport.d.ts +18 -0
  139. package/esm/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +38 -5
  140. package/esm/src/book-3.0/describeAgentPlannedMessageSchedule.d.ts +31 -0
  141. package/esm/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +1 -9
  142. package/esm/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.d.ts +28 -0
  143. package/esm/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.test.d.ts +1 -0
  144. package/esm/src/book-components/Chat/Chat/renderAgentProjectDiff.d.ts +13 -0
  145. package/esm/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.d.ts +16 -0
  146. package/esm/src/book-components/Chat/utils/agentProjectToolCall.d.ts +39 -0
  147. package/esm/src/book-components/Chat/utils/agentProjectToolCall.test.d.ts +1 -0
  148. package/esm/src/book-components/Chat/utils/externalSourceToolCall.d.ts +38 -0
  149. package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +6 -0
  150. package/esm/src/cli/cli-commands/coder/waitOptions.d.ts +12 -0
  151. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +15 -0
  152. package/esm/src/utils/agent-message-runtime/AgentMessageProjectChange.d.ts +76 -0
  153. package/esm/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.d.ts +45 -0
  154. package/esm/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +3 -0
  155. package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.d.ts +18 -0
  156. package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.test.d.ts +1 -0
  157. package/esm/src/utils/random/$generateBookBoilerplate.test.d.ts +1 -0
  158. package/esm/src/version.d.ts +1 -1
  159. package/package.json +1 -1
  160. package/src/book-2.0/agent-source/string_book.ts +1 -1
  161. package/src/book-2.0/book-language-documentation/czechBookLanguageManualDictionary.ts +6 -1
  162. package/src/book-2.0/book-language-documentation/englishBookLanguageManualDictionary.ts +6 -1
  163. package/src/book-3.0/AgentMessageRunReport.ts +59 -0
  164. package/src/book-3.0/AgentPlannedMessagesSidecar.ts +64 -9
  165. package/src/book-3.0/describeAgentPlannedMessageSchedule.ts +94 -0
  166. package/src/book-components/BookEditor/BookEditorMonaco.tsx +2 -0
  167. package/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.ts +3 -11
  168. package/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.ts +71 -0
  169. package/src/book-components/Chat/Chat/Chat.module.css +165 -18
  170. package/src/book-components/Chat/Chat/ChatMessageMap.tsx +0 -1
  171. package/src/book-components/Chat/Chat/renderAgentProjectDiff.tsx +85 -0
  172. package/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.tsx +206 -0
  173. package/src/book-components/Chat/Chat/renderToolCallDetails.tsx +8 -0
  174. package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +2 -3
  175. package/src/book-components/Chat/utils/agentProjectToolCall.ts +82 -5
  176. package/src/book-components/Chat/utils/externalSourceToolCall.ts +65 -0
  177. package/src/book-components/Chat/utils/getToolCallChipletInfo.ts +36 -0
  178. package/src/book-components/Chat/utils/renderMarkdown.ts +3 -10
  179. package/src/book-components/Chat/utils/timeoutToolCallPresentation.ts +12 -1
  180. package/src/cli/cli-commands/coder/ping.ts +35 -9
  181. package/src/cli/cli-commands/coder/waitOptions.ts +33 -0
  182. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +3 -1
  183. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +3 -1
  184. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +1 -4
  185. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +47 -3
  186. package/src/commands/FORMAT/formatCommandParser.ts +4 -2
  187. package/src/commitments/FROM/FROM.ts +13 -0
  188. package/src/other/templates/getTemplatesPipelineCollection.ts +819 -751
  189. package/src/utils/agent-message-runtime/AgentMessageProjectChange.ts +183 -0
  190. package/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.ts +80 -0
  191. package/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.ts +3 -0
  192. package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +2 -4
  193. package/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.ts +341 -0
  194. package/src/utils/random/$generateBookBoilerplate.ts +1 -1
  195. package/src/version.ts +2 -2
  196. package/src/versions.txt +5 -0
  197. package/umd/index.umd.js +950 -101
  198. package/umd/index.umd.js.map +1 -1
  199. package/umd/scripts/run-agent-messages/git/commitAgentProjectChanges.d.ts +15 -0
  200. package/umd/scripts/run-agent-messages/git/commitAnsweredMessageProjectChanges.d.ts +24 -0
  201. package/umd/scripts/run-agent-messages/git/ensureAgentProjectGitRepository.d.ts +20 -0
  202. package/umd/scripts/run-agent-messages/git/readAgentProjectCommitChange.d.ts +11 -0
  203. package/umd/scripts/run-agent-messages/git/runAgentProjectGitCommand.d.ts +29 -0
  204. package/umd/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +3 -3
  205. package/umd/scripts/run-agent-messages/messages/listAgentProjectDirectoryNames.d.ts +7 -0
  206. package/umd/scripts/run-agent-messages/messages/resolveAnsweredMessageTouches.d.ts +29 -0
  207. package/umd/scripts/run-codex-prompts/ping/pingCoderHarnessPeriodically.d.ts +22 -0
  208. package/umd/src/book-3.0/AgentMessageRunReport.d.ts +18 -0
  209. package/umd/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +38 -5
  210. package/umd/src/book-3.0/describeAgentPlannedMessageSchedule.d.ts +31 -0
  211. package/umd/src/book-components/BookEditor/createDeprecatedCommitmentDiagnostics.browser.d.ts +1 -9
  212. package/umd/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.d.ts +28 -0
  213. package/umd/src/book-components/BookEditor/createDuplicateFromCommitmentDiagnostics.test.d.ts +1 -0
  214. package/umd/src/book-components/Chat/Chat/renderAgentProjectDiff.d.ts +13 -0
  215. package/umd/src/book-components/Chat/Chat/renderAgentProjectToolCallDetails.d.ts +16 -0
  216. package/umd/src/book-components/Chat/utils/agentProjectToolCall.d.ts +39 -0
  217. package/umd/src/book-components/Chat/utils/agentProjectToolCall.test.d.ts +1 -0
  218. package/umd/src/book-components/Chat/utils/externalSourceToolCall.d.ts +38 -0
  219. package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +6 -0
  220. package/umd/src/cli/cli-commands/coder/waitOptions.d.ts +12 -0
  221. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +15 -0
  222. package/umd/src/utils/agent-message-runtime/AgentMessageProjectChange.d.ts +76 -0
  223. package/umd/src/utils/agent-message-runtime/AgentMessageTouchedExternalSource.d.ts +45 -0
  224. package/umd/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +3 -0
  225. package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.d.ts +18 -0
  226. package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedExternalSources.test.d.ts +1 -0
  227. package/umd/src/utils/random/$generateBookBoilerplate.test.d.ts +1 -0
  228. package/umd/src/version.d.ts +1 -1
  229. package/apps/agents-server/src/app/admin/task-manager/TaskManagerScopeTabs.tsx +0 -74
  230. package/esm/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +0 -15
  231. package/umd/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +0 -15
@@ -0,0 +1,186 @@
1
+ 'use client';
2
+
3
+ import type { PlannedMessageRecurrenceKind } from '@/src/utils/plannedMessageManager/resolvePlannedMessageRecurrenceKind';
4
+ import type { PlannedMessageManagerRecord, PlannedMessageManagerUpdatePayload } from '@/src/utils/plannedMessagesAdmin';
5
+
6
+ /**
7
+ * Unit one repeat interval is edited in.
8
+ *
9
+ * @private function of PlannedMessageManagerEditDialog
10
+ */
11
+ export type PlannedMessageIntervalUnit = 'minutes' | 'hours' | 'days';
12
+
13
+ /**
14
+ * Length of each editable interval unit, in milliseconds.
15
+ *
16
+ * @private function of PlannedMessageManagerEditDialog
17
+ */
18
+ const PLANNED_MESSAGE_INTERVAL_UNIT_MS: Record<PlannedMessageIntervalUnit, number> = {
19
+ minutes: 60_000,
20
+ hours: 60 * 60_000,
21
+ days: 24 * 60 * 60_000,
22
+ };
23
+
24
+ /**
25
+ * Units tried when showing a stored interval, from the longest to the shortest.
26
+ *
27
+ * @private function of PlannedMessageManagerEditDialog
28
+ */
29
+ const PLANNED_MESSAGE_INTERVAL_UNITS_BY_LENGTH: ReadonlyArray<PlannedMessageIntervalUnit> = [
30
+ 'days',
31
+ 'hours',
32
+ 'minutes',
33
+ ];
34
+
35
+ /**
36
+ * Editable form of one planned message.
37
+ *
38
+ * Every field is kept as the text the administrator typed, so what is validated is exactly what was
39
+ * entered — the shared planned-message service is the one place saying whether it is allowed.
40
+ *
41
+ * @private function of PlannedMessageManagerEditDialog
42
+ */
43
+ export type PlannedMessageEditForm = {
44
+ readonly message: string;
45
+ readonly recurrenceKind: PlannedMessageRecurrenceKind;
46
+ readonly intervalValue: string;
47
+ readonly intervalUnit: PlannedMessageIntervalUnit;
48
+ readonly cronExpression: string;
49
+ readonly startsAtLocal: string;
50
+ readonly endsAtLocal: string;
51
+ readonly maxRunCount: string;
52
+ };
53
+
54
+ /**
55
+ * Fills the edit form with the plan one planned message has today.
56
+ *
57
+ * @param plannedMessage - Planned message being edited.
58
+ * @returns Form describing its current plan.
59
+ *
60
+ * @private function of PlannedMessageManagerEditDialog
61
+ */
62
+ export function createPlannedMessageEditForm(plannedMessage: PlannedMessageManagerRecord): PlannedMessageEditForm {
63
+ const interval = resolvePlannedMessageIntervalFields(plannedMessage.recurrenceIntervalMs);
64
+
65
+ return {
66
+ message: plannedMessage.message || '',
67
+ recurrenceKind: plannedMessage.recurrenceKind,
68
+ intervalValue: interval.value,
69
+ intervalUnit: interval.unit,
70
+ cronExpression: plannedMessage.cronExpression || '',
71
+ startsAtLocal: formatIsoAsDateTimeLocalValue(plannedMessage.startsAt),
72
+ endsAtLocal: formatIsoAsDateTimeLocalValue(plannedMessage.endsAt),
73
+ maxRunCount: plannedMessage.maxRunCount === null ? '' : String(plannedMessage.maxRunCount),
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Turns the edit form into the change sent to the server.
79
+ *
80
+ * The whole plan is sent, not only what changed, so an emptied field really removes its bound instead
81
+ * of silently keeping the stored one.
82
+ *
83
+ * @param form - Form as the administrator filled it in.
84
+ * @returns Requested change.
85
+ *
86
+ * @private function of PlannedMessageManagerEditDialog
87
+ */
88
+ export function createPlannedMessageUpdatePayload(form: PlannedMessageEditForm): PlannedMessageManagerUpdatePayload {
89
+ return {
90
+ message: form.message.trim(),
91
+ milliseconds: form.recurrenceKind === 'INTERVAL' ? resolvePlannedMessageIntervalMs(form) : null,
92
+ cronExpression: form.recurrenceKind === 'CRON' ? form.cronExpression.trim() : null,
93
+ startsAt: parseDateTimeLocalValueAsIso(form.startsAtLocal),
94
+ endsAt: parseDateTimeLocalValueAsIso(form.endsAtLocal),
95
+ maxRunCount: form.maxRunCount.trim() === '' ? null : Number(form.maxRunCount),
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Resolves the repeat interval described by the form, in milliseconds.
101
+ *
102
+ * @param form - Form as the administrator filled it in.
103
+ * @returns Interval in milliseconds, or `null` when nothing usable was typed.
104
+ *
105
+ * @private function of PlannedMessageManagerEditDialog
106
+ */
107
+ function resolvePlannedMessageIntervalMs(form: PlannedMessageEditForm): number | null {
108
+ const intervalValue = Number(form.intervalValue);
109
+
110
+ if (!Number.isFinite(intervalValue)) {
111
+ return null;
112
+ }
113
+
114
+ return Math.floor(intervalValue * PLANNED_MESSAGE_INTERVAL_UNIT_MS[form.intervalUnit]);
115
+ }
116
+
117
+ /**
118
+ * Splits one stored interval into the longest unit that describes it as a whole number.
119
+ *
120
+ * @param recurrenceIntervalMs - Stored repeat interval.
121
+ * @returns Interval value and unit shown in the form.
122
+ *
123
+ * @private function of PlannedMessageManagerEditDialog
124
+ */
125
+ function resolvePlannedMessageIntervalFields(recurrenceIntervalMs: number | null): {
126
+ value: string;
127
+ unit: PlannedMessageIntervalUnit;
128
+ } {
129
+ if (!recurrenceIntervalMs) {
130
+ return { value: '', unit: 'minutes' };
131
+ }
132
+
133
+ for (const unit of PLANNED_MESSAGE_INTERVAL_UNITS_BY_LENGTH) {
134
+ const unitMs = PLANNED_MESSAGE_INTERVAL_UNIT_MS[unit];
135
+
136
+ if (recurrenceIntervalMs % unitMs === 0) {
137
+ return { value: String(recurrenceIntervalMs / unitMs), unit };
138
+ }
139
+ }
140
+
141
+ return { value: String(recurrenceIntervalMs / PLANNED_MESSAGE_INTERVAL_UNIT_MS.minutes), unit: 'minutes' };
142
+ }
143
+
144
+ /**
145
+ * Formats one stored moment for a `datetime-local` input, in the administrator's own time zone.
146
+ *
147
+ * @param isoDate - Stored moment, or `null`.
148
+ * @returns Local `YYYY-MM-DDTHH:mm` text, or an empty string.
149
+ *
150
+ * @private function of PlannedMessageManagerEditDialog
151
+ */
152
+ export function formatIsoAsDateTimeLocalValue(isoDate: string | null): string {
153
+ if (isoDate === null) {
154
+ return '';
155
+ }
156
+
157
+ const parsedDate = new Date(isoDate);
158
+
159
+ if (Number.isNaN(parsedDate.getTime())) {
160
+ return '';
161
+ }
162
+
163
+ const localDate = new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60_000);
164
+
165
+ return localDate.toISOString().slice(0, 16);
166
+ }
167
+
168
+ /**
169
+ * Reads one `datetime-local` input back as a stored moment.
170
+ *
171
+ * @param dateTimeLocalValue - Local `YYYY-MM-DDTHH:mm` text.
172
+ * @returns ISO moment, or `null` when the field was left empty.
173
+ *
174
+ * @private function of PlannedMessageManagerEditDialog
175
+ */
176
+ export function parseDateTimeLocalValueAsIso(dateTimeLocalValue: string): string | null {
177
+ const trimmedValue = dateTimeLocalValue.trim();
178
+
179
+ if (trimmedValue === '') {
180
+ return null;
181
+ }
182
+
183
+ const parsedDate = new Date(trimmedValue);
184
+
185
+ return Number.isNaN(parsedDate.getTime()) ? trimmedValue : parsedDate.toISOString();
186
+ }
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import { showAlert, showConfirm } from '@/src/components/AsyncDialogs/asyncDialogs';
4
+ import type { PlannedMessageManagerRecord } from '@/src/utils/plannedMessagesAdmin';
5
+ import { spaceTrim } from 'spacetrim';
6
+ import { resolvePlannedMessageText } from './plannedMessageManagerPresentation';
7
+
8
+ /**
9
+ * Asks the administrator to confirm cancelling one planned message.
10
+ *
11
+ * @param plannedMessage - Planned message about to be cancelled.
12
+ * @returns `true` when the administrator confirmed.
13
+ *
14
+ * @private shared dialog flow of the admin planned-message manager
15
+ */
16
+ export function confirmCancelPlannedMessage(plannedMessage: PlannedMessageManagerRecord): Promise<boolean> {
17
+ return showConfirm({
18
+ title: 'Cancel planned message',
19
+ message: spaceTrim(
20
+ (block) => `
21
+ Cancel this planned message of "${plannedMessage.agentName || plannedMessage.agentPermanentId}"?
22
+
23
+ ${block(resolvePlannedMessageText(plannedMessage))}
24
+
25
+ It never wakes the agent again, and stays listed as cancelled.
26
+ `,
27
+ ),
28
+ confirmLabel: 'Cancel planned message',
29
+ cancelLabel: 'Keep it',
30
+ }).catch(() => false);
31
+ }
32
+
33
+ /**
34
+ * Surfaces a failed planned-message action to the administrator.
35
+ *
36
+ * @param title - Short title naming what failed.
37
+ * @param error - Error thrown by the action.
38
+ *
39
+ * @private shared dialog flow of the admin planned-message manager
40
+ */
41
+ export async function showPlannedMessageActionFailure(title: string, error: unknown): Promise<void> {
42
+ await showAlert({
43
+ title,
44
+ message: error instanceof Error ? error.message : 'The planned-message action failed.',
45
+ }).catch(() => undefined);
46
+ }
@@ -0,0 +1,188 @@
1
+ 'use client';
2
+
3
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
4
+ import type { PlannedMessageEndReason } from '@/src/utils/plannedMessageManager/resolvePlannedMessageEndReason';
5
+ import type { PlannedMessageLifecycle } from '@/src/utils/plannedMessageManager/resolvePlannedMessageLifecycle';
6
+ import type { PlannedMessageManagerRecord } from '@/src/utils/plannedMessagesAdmin';
7
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
8
+ import { describeAgentPlannedMessageSchedule } from '../../../../../../src/book-3.0/describeAgentPlannedMessageSchedule';
9
+ import { formatTimeoutDurationHuman } from '../../../../../../src/book-components/Chat/utils/timeoutToolCallPresentation';
10
+
11
+ /**
12
+ * Human label of each planned-message stage.
13
+ *
14
+ * @private shared presentation of the admin planned-message manager
15
+ */
16
+ export const PLANNED_MESSAGE_LIFECYCLE_LABELS: Record<PlannedMessageLifecycle, string> = {
17
+ ONGOING: 'Ongoing',
18
+ SCHEDULED: 'Scheduled',
19
+ NOT_STARTED: 'Not started',
20
+ PAUSED: 'Paused',
21
+ CANCELLED: 'Cancelled',
22
+ ENDED: 'Ended',
23
+ FAILED: 'Failed',
24
+ };
25
+
26
+ /**
27
+ * What each planned-message stage means, shown as the badge tooltip.
28
+ *
29
+ * @private shared presentation of the admin planned-message manager
30
+ */
31
+ const PLANNED_MESSAGE_LIFECYCLE_TITLES: Record<PlannedMessageLifecycle, string> = {
32
+ ONGOING: 'Waking its agent right now',
33
+ SCHEDULED: 'Waiting for its next wake-up',
34
+ NOT_STARTED: 'Planned, but its starting date is still ahead',
35
+ PAUSED: 'Held back, so it does not wake its agent until it is resumed',
36
+ CANCELLED: 'Cancelled, so it never wakes its agent again',
37
+ ENDED: 'Over, because its plan ran out',
38
+ FAILED: 'Stopped after its wake-up could not be delivered',
39
+ };
40
+
41
+ /**
42
+ * Badge color classes of each planned-message stage.
43
+ *
44
+ * @private shared presentation of the admin planned-message manager
45
+ */
46
+ const PLANNED_MESSAGE_LIFECYCLE_CLASS_MAP: Record<PlannedMessageLifecycle, string> = {
47
+ ONGOING: 'border-blue-200 bg-blue-50 text-blue-700',
48
+ SCHEDULED: 'border-slate-200 bg-slate-50 text-slate-700',
49
+ NOT_STARTED: 'border-indigo-200 bg-indigo-50 text-indigo-700',
50
+ PAUSED: 'border-orange-200 bg-orange-50 text-orange-700',
51
+ CANCELLED: 'border-gray-200 bg-gray-100 text-gray-700',
52
+ ENDED: 'border-emerald-200 bg-emerald-50 text-emerald-700',
53
+ FAILED: 'border-rose-200 bg-rose-50 text-rose-700',
54
+ };
55
+
56
+ /**
57
+ * Human explanation of why one planned message is over.
58
+ *
59
+ * @private shared presentation of the admin planned-message manager
60
+ */
61
+ const PLANNED_MESSAGE_END_REASON_LABELS: Record<NonNullable<PlannedMessageEndReason>, string> = {
62
+ MAX_RUNS_REACHED: 'all planned runs done',
63
+ ENDING_DATE_PASSED: 'ending date passed',
64
+ SINGLE_RUN_DONE: 'single wake-up done',
65
+ };
66
+
67
+ /**
68
+ * Compact badge naming the stage one planned message is in.
69
+ *
70
+ * @private shared presentation of the admin planned-message manager
71
+ */
72
+ export function PlannedMessageLifecycleBadge({ lifecycle }: { lifecycle: PlannedMessageLifecycle }) {
73
+ return (
74
+ <span
75
+ className={`rounded-full border px-2 py-0.5 text-[11px] font-semibold ${PLANNED_MESSAGE_LIFECYCLE_CLASS_MAP[lifecycle]}`}
76
+ title={PLANNED_MESSAGE_LIFECYCLE_TITLES[lifecycle]}
77
+ >
78
+ {PLANNED_MESSAGE_LIFECYCLE_LABELS[lifecycle]}
79
+ </span>
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Compact badge marking the planned messages an agent wrote for itself in its goal chat.
85
+ *
86
+ * @private shared presentation of the admin planned-message manager
87
+ */
88
+ export function PlannedMessageGoalChatBadge({ plannedMessage }: { plannedMessage: PlannedMessageManagerRecord }) {
89
+ if (!plannedMessage.isAgentGoalChat) {
90
+ return null;
91
+ }
92
+
93
+ return (
94
+ <span
95
+ className="rounded-full border border-indigo-200 bg-indigo-50 px-2 py-0.5 text-[11px] font-semibold text-indigo-700"
96
+ title="Planned by the agent itself in its goal chat, not by a user"
97
+ >
98
+ Goal chat
99
+ </span>
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Formats how often one planned message wakes its agent.
105
+ *
106
+ * @param plannedMessage - Planned message being described.
107
+ * @returns Short frequency text such as `Every 5 minutes`.
108
+ *
109
+ * @private shared presentation of the admin planned-message manager
110
+ */
111
+ export function formatPlannedMessageFrequency(plannedMessage: PlannedMessageManagerRecord): string {
112
+ if (plannedMessage.recurrenceKind === 'CRON') {
113
+ return `Cron ${plannedMessage.cronExpression}`;
114
+ }
115
+
116
+ if (plannedMessage.recurrenceKind === 'INTERVAL') {
117
+ return `Every ${formatTimeoutDurationHuman(plannedMessage.recurrenceIntervalMs || 0)}`;
118
+ }
119
+
120
+ return 'Once';
121
+ }
122
+
123
+ /**
124
+ * Describes the whole plan of one planned message in one sentence.
125
+ *
126
+ * The wording comes from the very same helper the agent itself is told its plan with, so the manager
127
+ * and the agent never describe one plan in two different ways.
128
+ *
129
+ * @param plannedMessage - Planned message being described.
130
+ * @returns Sentence fragment describing the plan.
131
+ *
132
+ * @private shared presentation of the admin planned-message manager
133
+ */
134
+ export function describePlannedMessagePlan(plannedMessage: PlannedMessageManagerRecord): string {
135
+ return describeAgentPlannedMessageSchedule({
136
+ intervalMs: plannedMessage.recurrenceIntervalMs,
137
+ cronExpression: plannedMessage.cronExpression,
138
+ startsAt: plannedMessage.startsAt,
139
+ endsAt: plannedMessage.endsAt,
140
+ maxRunCount: plannedMessage.maxRunCount,
141
+ runCount: plannedMessage.runCount,
142
+ dueAt: plannedMessage.dueAt,
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Formats why one planned message is over.
148
+ *
149
+ * @param endReason - Reason the plan is over.
150
+ * @returns Short explanation, or `null` when the plan does not explain its own end.
151
+ *
152
+ * @private shared presentation of the admin planned-message manager
153
+ */
154
+ export function formatPlannedMessageEndReason(endReason: PlannedMessageEndReason): string | null {
155
+ return endReason === null ? null : PLANNED_MESSAGE_END_REASON_LABELS[endReason];
156
+ }
157
+
158
+ /**
159
+ * Formats how many wake-ups one planned message already did, out of how many it may do.
160
+ *
161
+ * @param plannedMessage - Planned message being described.
162
+ * @returns Run counter such as `2 / 10`.
163
+ *
164
+ * @private shared presentation of the admin planned-message manager
165
+ */
166
+ export function formatPlannedMessageRunCount(plannedMessage: PlannedMessageManagerRecord): string {
167
+ return plannedMessage.maxRunCount === null
168
+ ? String(plannedMessage.runCount)
169
+ : `${plannedMessage.runCount} / ${plannedMessage.maxRunCount}`;
170
+ }
171
+
172
+ /**
173
+ * Formats one timestamp for compact planned-message display.
174
+ *
175
+ * @private shared presentation of the admin planned-message manager
176
+ */
177
+ export function formatPlannedMessageDateTime(value: string | null, language: ServerLanguageCode): string {
178
+ return formatServerLanguageHumanReadableDate(value, language, { fallbackLabel: '-' });
179
+ }
180
+
181
+ /**
182
+ * Resolves the text shown for one planned message that has no message of its own.
183
+ *
184
+ * @private shared presentation of the admin planned-message manager
185
+ */
186
+ export function resolvePlannedMessageText(plannedMessage: PlannedMessageManagerRecord): string {
187
+ return plannedMessage.message || 'Planned wake-up without a message';
188
+ }
@@ -0,0 +1,115 @@
1
+ 'use client';
2
+
3
+ import { isPlannedMessageStillPlanned } from '@/src/utils/plannedMessageManager/isPlannedMessageStillPlanned';
4
+ import type { PlannedMessageLifecycle } from '@/src/utils/plannedMessageManager/resolvePlannedMessageLifecycle';
5
+ import type { PlannedMessageManagerRecord, PlannedMessageManagerSortField } from '@/src/utils/plannedMessagesAdmin';
6
+ import type { AdminTableSortableValue, AdminTableSortOrder } from '../_components/adminTableSorting';
7
+ import { resolvePlannedMessageText } from './plannedMessageManagerPresentation';
8
+
9
+ /**
10
+ * Order the stages are sorted in, from the most immediate to the most finished.
11
+ *
12
+ * @private function of PlannedMessageManagerClient
13
+ */
14
+ const PLANNED_MESSAGE_LIFECYCLE_SORT_RANK: Record<PlannedMessageLifecycle, number> = {
15
+ ONGOING: 0,
16
+ SCHEDULED: 1,
17
+ NOT_STARTED: 2,
18
+ PAUSED: 3,
19
+ CANCELLED: 4,
20
+ ENDED: 5,
21
+ FAILED: 6,
22
+ };
23
+
24
+ /**
25
+ * Sort value standing for a planned message that repeats on a cron.
26
+ *
27
+ * A cron cadence cannot be compared with a fixed interval without evaluating it, so cron-driven
28
+ * messages are sorted after every fixed interval but before the ones that never repeat.
29
+ *
30
+ * @private function of PlannedMessageManagerClient
31
+ */
32
+ const CRON_PLANNED_MESSAGE_FREQUENCY_SORT_VALUE = Number.MAX_SAFE_INTEGER - 1;
33
+
34
+ /**
35
+ * Sort value standing for a planned message that wakes its agent exactly once.
36
+ *
37
+ * @private function of PlannedMessageManagerClient
38
+ */
39
+ const SINGLE_RUN_PLANNED_MESSAGE_FREQUENCY_SORT_VALUE = Number.MAX_SAFE_INTEGER;
40
+
41
+ /**
42
+ * Resolves the comparable value of one planned message for the active sort column.
43
+ *
44
+ * Sorting by frequency puts the most frequently repeating planned messages first, because that is the
45
+ * order an administrator looks for when asking which plan is costing the most wake-ups.
46
+ *
47
+ * @param plannedMessage - Planned message being sorted.
48
+ * @param sortBy - Column the table is sorted by.
49
+ * @returns Comparable value of the planned message.
50
+ *
51
+ * @private function of PlannedMessageManagerClient
52
+ */
53
+ export function resolvePlannedMessageSortValue(
54
+ plannedMessage: PlannedMessageManagerRecord,
55
+ sortBy: PlannedMessageManagerSortField,
56
+ ): AdminTableSortableValue {
57
+ if (sortBy === 'plannedMessage') {
58
+ return resolvePlannedMessageText(plannedMessage);
59
+ }
60
+
61
+ if (sortBy === 'agent') {
62
+ return plannedMessage.agentName || plannedMessage.agentPermanentId;
63
+ }
64
+
65
+ if (sortBy === 'frequency') {
66
+ return resolvePlannedMessageFrequencySortValue(plannedMessage);
67
+ }
68
+
69
+ if (sortBy === 'nextRun') {
70
+ // Note: A planned message that is over has no wake-up ahead, so it sorts after every one that has
71
+ return isPlannedMessageStillPlanned(plannedMessage.lifecycle) ? new Date(plannedMessage.dueAt) : null;
72
+ }
73
+
74
+ if (sortBy === 'lastRun') {
75
+ return plannedMessage.lastFiredAt === null ? null : new Date(plannedMessage.lastFiredAt);
76
+ }
77
+
78
+ if (sortBy === 'runs') {
79
+ return plannedMessage.runCount;
80
+ }
81
+
82
+ return PLANNED_MESSAGE_LIFECYCLE_SORT_RANK[plannedMessage.lifecycle];
83
+ }
84
+
85
+ /**
86
+ * Resolves the initial direction used when switching planned-message sort columns.
87
+ *
88
+ * @param sortBy - Column the table is being sorted by.
89
+ * @returns Direction that reads most naturally for that column.
90
+ *
91
+ * @private function of PlannedMessageManagerClient
92
+ */
93
+ export function resolvePlannedMessageManagerDefaultSortOrder(
94
+ sortBy: PlannedMessageManagerSortField,
95
+ ): AdminTableSortOrder {
96
+ return sortBy === 'lastRun' || sortBy === 'runs' ? 'desc' : 'asc';
97
+ }
98
+
99
+ /**
100
+ * Resolves how often one planned message repeats, as a comparable period in milliseconds.
101
+ *
102
+ * @param plannedMessage - Planned message being sorted.
103
+ * @returns Repeat period, or a sentinel standing for cron-driven and one-off messages.
104
+ *
105
+ * @private function of `resolvePlannedMessageSortValue`
106
+ */
107
+ function resolvePlannedMessageFrequencySortValue(plannedMessage: PlannedMessageManagerRecord): number {
108
+ if (plannedMessage.recurrenceKind === 'INTERVAL') {
109
+ return plannedMessage.recurrenceIntervalMs || 0;
110
+ }
111
+
112
+ return plannedMessage.recurrenceKind === 'CRON'
113
+ ? CRON_PLANNED_MESSAGE_FREQUENCY_SORT_VALUE
114
+ : SINGLE_RUN_PLANNED_MESSAGE_FREQUENCY_SORT_VALUE;
115
+ }
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+
3
+ import { $fetchAdminPlannedMessages, type PlannedMessageManagerRecord } from '@/src/utils/plannedMessagesAdmin';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+
6
+ /**
7
+ * Default auto-refresh cadence of the planned-message manager.
8
+ *
9
+ * @private function of PlannedMessageManagerClient
10
+ */
11
+ const DEFAULT_PLANNED_MESSAGE_POLL_INTERVAL_MS = 10_000;
12
+
13
+ /**
14
+ * Live planned-message listing shared by every part of the manager.
15
+ *
16
+ * @private function of PlannedMessageManagerClient
17
+ */
18
+ export type PlannedMessageManagerData = {
19
+ readonly plannedMessages: Array<PlannedMessageManagerRecord>;
20
+ readonly generatedAt: string | null;
21
+ readonly hasMore: boolean;
22
+ readonly isLoading: boolean;
23
+ readonly isRefreshing: boolean;
24
+ readonly error: string | null;
25
+ readonly pollIntervalMs: number;
26
+ readonly refreshNow: () => void;
27
+ readonly updatePollIntervalMs: (value: string) => void;
28
+ };
29
+
30
+ /**
31
+ * Loads every planned message of the server and keeps the listing fresh.
32
+ *
33
+ * @returns Live planned-message listing with its refresh controls.
34
+ *
35
+ * @private function of PlannedMessageManagerClient
36
+ */
37
+ export function usePlannedMessageManagerData(): PlannedMessageManagerData {
38
+ const [plannedMessages, setPlannedMessages] = useState<Array<PlannedMessageManagerRecord>>([]);
39
+ const [generatedAt, setGeneratedAt] = useState<string | null>(null);
40
+ const [hasMore, setHasMore] = useState(false);
41
+ const [isLoading, setIsLoading] = useState(true);
42
+ const [isRefreshing, setIsRefreshing] = useState(false);
43
+ const [error, setError] = useState<string | null>(null);
44
+ const [pollIntervalMs, setPollIntervalMs] = useState(DEFAULT_PLANNED_MESSAGE_POLL_INTERVAL_MS);
45
+ const [refreshNonce, setRefreshNonce] = useState(0);
46
+ const hasLoadedOnceRef = useRef(false);
47
+
48
+ const refreshNow = useCallback((): void => {
49
+ setRefreshNonce((currentNonce) => currentNonce + 1);
50
+ }, []);
51
+
52
+ const updatePollIntervalMs = useCallback((value: string): void => {
53
+ const parsedPollIntervalMs = Number.parseInt(value, 10);
54
+ setPollIntervalMs(Number.isFinite(parsedPollIntervalMs) ? parsedPollIntervalMs : 0);
55
+ }, []);
56
+
57
+ useEffect(() => {
58
+ let isCancelled = false;
59
+
60
+ async function loadPlannedMessages(): Promise<void> {
61
+ if (hasLoadedOnceRef.current) {
62
+ setIsRefreshing(true);
63
+ }
64
+
65
+ try {
66
+ const response = await $fetchAdminPlannedMessages();
67
+
68
+ if (isCancelled) {
69
+ return;
70
+ }
71
+
72
+ setPlannedMessages(response.items);
73
+ setGeneratedAt(response.generatedAt);
74
+ setHasMore(response.hasMore);
75
+ setError(null);
76
+ } catch (loadError) {
77
+ if (!isCancelled) {
78
+ setError(loadError instanceof Error ? loadError.message : 'Failed to load planned messages.');
79
+ }
80
+ } finally {
81
+ if (!isCancelled) {
82
+ setIsLoading(false);
83
+ setIsRefreshing(false);
84
+ hasLoadedOnceRef.current = true;
85
+ }
86
+ }
87
+ }
88
+
89
+ void loadPlannedMessages();
90
+
91
+ return () => {
92
+ isCancelled = true;
93
+ };
94
+ }, [refreshNonce]);
95
+
96
+ useEffect(() => {
97
+ if (pollIntervalMs <= 0) {
98
+ return;
99
+ }
100
+
101
+ const refreshTimer = window.setInterval(() => refreshNow(), pollIntervalMs);
102
+ return () => window.clearInterval(refreshTimer);
103
+ }, [pollIntervalMs, refreshNow]);
104
+
105
+ return {
106
+ plannedMessages,
107
+ generatedAt,
108
+ hasMore,
109
+ isLoading,
110
+ isRefreshing,
111
+ error,
112
+ pollIntervalMs,
113
+ refreshNow,
114
+ updatePollIntervalMs,
115
+ };
116
+ }