@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,56 @@
1
+ import type { PlannedMessageManagerRecord } from '../plannedMessagesAdmin';
2
+
3
+ /**
4
+ * One agent offered by the planned-message agent filter.
5
+ *
6
+ * @private internal admin utility of Agents Server
7
+ */
8
+ export type PlannedMessageAgentOption = {
9
+ readonly agentPermanentId: string;
10
+ readonly agentName: string | null;
11
+ readonly plannedMessageCount: number;
12
+ };
13
+
14
+ /**
15
+ * Collects the agents that really have planned messages, for the agent filter.
16
+ *
17
+ * Only agents present in the listing are offered, so the filter can never select an empty result.
18
+ *
19
+ * @param plannedMessages - All planned messages of the server.
20
+ * @returns Agents sorted by their displayed name.
21
+ *
22
+ * @private internal admin utility of Agents Server
23
+ */
24
+ export function collectPlannedMessageAgentOptions(
25
+ plannedMessages: ReadonlyArray<PlannedMessageManagerRecord>,
26
+ ): Array<PlannedMessageAgentOption> {
27
+ const optionsByAgentPermanentId = new Map<string, PlannedMessageAgentOption>();
28
+
29
+ for (const plannedMessage of plannedMessages) {
30
+ const existingOption = optionsByAgentPermanentId.get(plannedMessage.agentPermanentId);
31
+
32
+ optionsByAgentPermanentId.set(plannedMessage.agentPermanentId, {
33
+ agentPermanentId: plannedMessage.agentPermanentId,
34
+ agentName: existingOption?.agentName ?? plannedMessage.agentName,
35
+ plannedMessageCount: (existingOption?.plannedMessageCount ?? 0) + 1,
36
+ });
37
+ }
38
+
39
+ return [...optionsByAgentPermanentId.values()].sort((leftOption, rightOption) =>
40
+ resolvePlannedMessageAgentOptionLabel(leftOption).localeCompare(
41
+ resolvePlannedMessageAgentOptionLabel(rightOption),
42
+ ),
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Resolves the label of one agent filter option.
48
+ *
49
+ * @param option - Agent offered by the filter.
50
+ * @returns Agent name, falling back to its permanent id.
51
+ *
52
+ * @private internal admin utility of Agents Server
53
+ */
54
+ export function resolvePlannedMessageAgentOptionLabel(option: PlannedMessageAgentOption): string {
55
+ return option.agentName || option.agentPermanentId;
56
+ }
@@ -0,0 +1,104 @@
1
+ import type { PlannedMessageManagerRecord, PlannedMessageManagerView } from '../plannedMessagesAdmin';
2
+ import { PLANNED_MESSAGE_LIFECYCLES_BY_VIEW } from './filterPlannedMessages';
3
+ import type { PlannedMessageLifecycle } from './resolvePlannedMessageLifecycle';
4
+
5
+ /**
6
+ * How many planned messages stand in each stage right now.
7
+ *
8
+ * @private internal admin utility of Agents Server
9
+ */
10
+ export type PlannedMessageManagerCounters = {
11
+ readonly countByLifecycle: Record<PlannedMessageLifecycle, number>;
12
+ readonly totalCount: number;
13
+
14
+ /**
15
+ * Earliest moment any planned message is going to wake its agent, or `null` when none will.
16
+ */
17
+ readonly nextDueAt: string | null;
18
+ };
19
+
20
+ /**
21
+ * Stages that still have a wake-up ahead of them.
22
+ *
23
+ * @private constant of `createPlannedMessageManagerCounters`
24
+ */
25
+ const UPCOMING_PLANNED_MESSAGE_LIFECYCLES: ReadonlyArray<PlannedMessageLifecycle> = ['SCHEDULED', 'NOT_STARTED'];
26
+
27
+ /**
28
+ * Counts the planned messages of each stage and finds the next wake-up ahead.
29
+ *
30
+ * @param plannedMessages - All planned messages of the server.
31
+ * @returns Counters rendered above the planned-message table.
32
+ *
33
+ * @private internal admin utility of Agents Server
34
+ */
35
+ export function createPlannedMessageManagerCounters(
36
+ plannedMessages: ReadonlyArray<PlannedMessageManagerRecord>,
37
+ ): PlannedMessageManagerCounters {
38
+ const countByLifecycle: Record<PlannedMessageLifecycle, number> = {
39
+ ONGOING: 0,
40
+ SCHEDULED: 0,
41
+ NOT_STARTED: 0,
42
+ PAUSED: 0,
43
+ CANCELLED: 0,
44
+ ENDED: 0,
45
+ FAILED: 0,
46
+ };
47
+ let nextDueAt: string | null = null;
48
+
49
+ for (const plannedMessage of plannedMessages) {
50
+ countByLifecycle[plannedMessage.lifecycle] += 1;
51
+
52
+ if (UPCOMING_PLANNED_MESSAGE_LIFECYCLES.includes(plannedMessage.lifecycle)) {
53
+ nextDueAt = resolveEarlierDate(nextDueAt, plannedMessage.dueAt);
54
+ }
55
+ }
56
+
57
+ return {
58
+ countByLifecycle,
59
+ totalCount: plannedMessages.length,
60
+ nextDueAt,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Counts the planned messages one manager view lists.
66
+ *
67
+ * @param counters - Counters of every stage.
68
+ * @param view - View whose size is asked for.
69
+ * @returns Number of planned messages the view lists before any other filter narrows it.
70
+ *
71
+ * @private internal admin utility of Agents Server
72
+ */
73
+ export function resolvePlannedMessageManagerViewCount(
74
+ counters: PlannedMessageManagerCounters,
75
+ view: PlannedMessageManagerView,
76
+ ): number {
77
+ return PLANNED_MESSAGE_LIFECYCLES_BY_VIEW[view].reduce(
78
+ (viewCount, lifecycle) => viewCount + counters.countByLifecycle[lifecycle],
79
+ 0,
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Keeps whichever of two moments comes first.
85
+ *
86
+ * @param currentDate - Earliest moment found so far, or `null`.
87
+ * @param candidateDate - Moment being considered.
88
+ * @returns Earlier of the two moments.
89
+ *
90
+ * @private function of `createPlannedMessageManagerCounters`
91
+ */
92
+ function resolveEarlierDate(currentDate: string | null, candidateDate: string): string | null {
93
+ const candidateTime = Date.parse(candidateDate);
94
+
95
+ if (!Number.isFinite(candidateTime)) {
96
+ return currentDate;
97
+ }
98
+
99
+ if (currentDate === null) {
100
+ return candidateDate;
101
+ }
102
+
103
+ return candidateTime < Date.parse(currentDate) ? candidateDate : currentDate;
104
+ }
@@ -0,0 +1,191 @@
1
+ import type {
2
+ PlannedMessageManagerLastRunFilter,
3
+ PlannedMessageManagerRecord,
4
+ PlannedMessageManagerRecurrenceFilter,
5
+ PlannedMessageManagerView,
6
+ } from '../plannedMessagesAdmin';
7
+ import type { PlannedMessageLifecycle } from './resolvePlannedMessageLifecycle';
8
+
9
+ /**
10
+ * Stages shown by each planned-message manager view.
11
+ *
12
+ * This is the single place deciding what "active", "finished",... mean, so the tabs, the counters
13
+ * shown on them, and the listed rows can never describe different sets of planned messages.
14
+ *
15
+ * @private internal admin utility of Agents Server
16
+ */
17
+ export const PLANNED_MESSAGE_LIFECYCLES_BY_VIEW: Record<
18
+ PlannedMessageManagerView,
19
+ ReadonlyArray<PlannedMessageLifecycle>
20
+ > = {
21
+ active: ['ONGOING', 'SCHEDULED', 'NOT_STARTED', 'PAUSED'],
22
+ scheduled: ['SCHEDULED'],
23
+ 'not-started': ['NOT_STARTED'],
24
+ ongoing: ['ONGOING'],
25
+ paused: ['PAUSED'],
26
+ finished: ['CANCELLED', 'ENDED', 'FAILED'],
27
+ all: ['ONGOING', 'SCHEDULED', 'NOT_STARTED', 'PAUSED', 'CANCELLED', 'ENDED', 'FAILED'],
28
+ };
29
+
30
+ /**
31
+ * Age of the newest wake-up still matching each last-run filter, in milliseconds.
32
+ *
33
+ * @private constant of `filterPlannedMessages`
34
+ */
35
+ const PLANNED_MESSAGE_LAST_RUN_WINDOW_MS: Record<'hour' | 'day' | 'week', number> = {
36
+ hour: 60 * 60 * 1000,
37
+ day: 24 * 60 * 60 * 1000,
38
+ week: 7 * 24 * 60 * 60 * 1000,
39
+ };
40
+
41
+ /**
42
+ * Filter value meaning "do not narrow by this parameter".
43
+ *
44
+ * @private constant of `filterPlannedMessages`
45
+ */
46
+ const PLANNED_MESSAGE_FILTER_ANY_VALUE = 'all';
47
+
48
+ /**
49
+ * Every parameter the planned-message manager can narrow its listing by.
50
+ *
51
+ * @private internal admin utility of Agents Server
52
+ */
53
+ export type PlannedMessageManagerFilters = {
54
+ readonly view: PlannedMessageManagerView;
55
+
56
+ /**
57
+ * Permanent id of the agent whose planned messages are listed, or `all` for every agent.
58
+ */
59
+ readonly agentPermanentId: string;
60
+
61
+ readonly recurrence: PlannedMessageManagerRecurrenceFilter;
62
+ readonly lastRun: PlannedMessageManagerLastRunFilter;
63
+
64
+ /**
65
+ * Free text matched against the message, its ids, its agent, and its owner.
66
+ */
67
+ readonly search: string;
68
+ };
69
+
70
+ /**
71
+ * Keeps only the planned messages matching every active filter.
72
+ *
73
+ * @param plannedMessages - All planned messages of the server.
74
+ * @param filters - Parameters the administrator narrowed the listing by.
75
+ * @param atDate - Moment the last-run windows are measured from.
76
+ * @returns Matching planned messages, in the order they were given.
77
+ *
78
+ * @private internal admin utility of Agents Server
79
+ */
80
+ export function filterPlannedMessages(
81
+ plannedMessages: ReadonlyArray<PlannedMessageManagerRecord>,
82
+ filters: PlannedMessageManagerFilters,
83
+ atDate: Date,
84
+ ): Array<PlannedMessageManagerRecord> {
85
+ const normalizedSearch = filters.search.trim().toLowerCase();
86
+
87
+ return plannedMessages.filter(
88
+ (plannedMessage) =>
89
+ isPlannedMessageInView(plannedMessage, filters.view) &&
90
+ isPlannedMessageOfAgent(plannedMessage, filters.agentPermanentId) &&
91
+ isPlannedMessageOfRecurrence(plannedMessage, filters.recurrence) &&
92
+ isPlannedMessageLastRunInWindow(plannedMessage, filters.lastRun, atDate) &&
93
+ isPlannedMessageMatchingSearch(plannedMessage, normalizedSearch),
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Determines whether one planned message belongs to the selected view.
99
+ *
100
+ * @private function of `filterPlannedMessages`
101
+ */
102
+ function isPlannedMessageInView(
103
+ plannedMessage: PlannedMessageManagerRecord,
104
+ view: PlannedMessageManagerView,
105
+ ): boolean {
106
+ return PLANNED_MESSAGE_LIFECYCLES_BY_VIEW[view].includes(plannedMessage.lifecycle);
107
+ }
108
+
109
+ /**
110
+ * Determines whether one planned message belongs to the selected agent.
111
+ *
112
+ * @private function of `filterPlannedMessages`
113
+ */
114
+ function isPlannedMessageOfAgent(plannedMessage: PlannedMessageManagerRecord, agentPermanentId: string): boolean {
115
+ return (
116
+ agentPermanentId === PLANNED_MESSAGE_FILTER_ANY_VALUE ||
117
+ plannedMessage.agentPermanentId.toLowerCase() === agentPermanentId.toLowerCase()
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Determines whether one planned message repeats the selected way.
123
+ *
124
+ * @private function of `filterPlannedMessages`
125
+ */
126
+ function isPlannedMessageOfRecurrence(
127
+ plannedMessage: PlannedMessageManagerRecord,
128
+ recurrence: PlannedMessageManagerRecurrenceFilter,
129
+ ): boolean {
130
+ return recurrence === PLANNED_MESSAGE_FILTER_ANY_VALUE || plannedMessage.recurrenceKind === recurrence;
131
+ }
132
+
133
+ /**
134
+ * Determines whether one planned message last woke its agent inside the selected window.
135
+ *
136
+ * @private function of `filterPlannedMessages`
137
+ */
138
+ function isPlannedMessageLastRunInWindow(
139
+ plannedMessage: PlannedMessageManagerRecord,
140
+ lastRun: PlannedMessageManagerLastRunFilter,
141
+ atDate: Date,
142
+ ): boolean {
143
+ if (lastRun === PLANNED_MESSAGE_FILTER_ANY_VALUE) {
144
+ return true;
145
+ }
146
+
147
+ if (plannedMessage.lastFiredAt === null) {
148
+ return lastRun === 'never';
149
+ }
150
+
151
+ if (lastRun === 'never') {
152
+ return false;
153
+ }
154
+
155
+ const lastFiredAtTime = Date.parse(plannedMessage.lastFiredAt);
156
+ if (!Number.isFinite(lastFiredAtTime)) {
157
+ return false;
158
+ }
159
+
160
+ const lastRunAgeMs = atDate.getTime() - lastFiredAtTime;
161
+
162
+ if (lastRun === 'older') {
163
+ return lastRunAgeMs > PLANNED_MESSAGE_LAST_RUN_WINDOW_MS.week;
164
+ }
165
+
166
+ return lastRunAgeMs <= PLANNED_MESSAGE_LAST_RUN_WINDOW_MS[lastRun];
167
+ }
168
+
169
+ /**
170
+ * Determines whether one planned message matches the free-text search.
171
+ *
172
+ * @private function of `filterPlannedMessages`
173
+ */
174
+ function isPlannedMessageMatchingSearch(plannedMessage: PlannedMessageManagerRecord, normalizedSearch: string): boolean {
175
+ if (normalizedSearch.length === 0) {
176
+ return true;
177
+ }
178
+
179
+ const searchableValues = [
180
+ plannedMessage.message,
181
+ plannedMessage.timeoutId,
182
+ plannedMessage.agentPermanentId,
183
+ plannedMessage.agentName,
184
+ plannedMessage.chatId,
185
+ plannedMessage.username,
186
+ String(plannedMessage.userId),
187
+ plannedMessage.cronExpression,
188
+ ];
189
+
190
+ return searchableValues.some((value) => typeof value === 'string' && value.toLowerCase().includes(normalizedSearch));
191
+ }
@@ -0,0 +1,55 @@
1
+ import { loadAgentNamesByPermanentId, loadUsernamesByUserId } from '../adminEntityLookups';
2
+ import type { PlannedMessageManagerListResponse } from '../plannedMessagesAdmin';
3
+ import { listAllUserChatTimeouts } from '../userChatTimeout';
4
+ import { ensureUserChatTimeoutWorkerBootstrapped } from '../userChatTimeout/ensureUserChatTimeoutWorkerBootstrapped';
5
+ import { mapPlannedMessageManagerRecord } from './mapPlannedMessageManagerRecord';
6
+
7
+ /**
8
+ * Largest number of planned messages the manager loads at once.
9
+ *
10
+ * The whole set is sent to the browser so filtering, counting, and sorting all see the same rows;
11
+ * this cap keeps that promise affordable on a server with a long planning history.
12
+ *
13
+ * @private constant of `getPlannedMessageManagerResponse`
14
+ */
15
+ const MAX_PLANNED_MESSAGE_MANAGER_ITEMS = 1_000;
16
+
17
+ /**
18
+ * Builds the admin planned-message manager payload from live durable timeout state.
19
+ *
20
+ * Every planned message of every agent and every user of this server is listed, whether it is still
21
+ * ahead, firing right now, or already over.
22
+ *
23
+ * @returns Planned messages with the moment they were read.
24
+ *
25
+ * @private internal admin utility of Agents Server
26
+ */
27
+ export async function getPlannedMessageManagerResponse(): Promise<PlannedMessageManagerListResponse> {
28
+ ensureUserChatTimeoutWorkerBootstrapped();
29
+
30
+ // Note: One row above the cap is loaded, purely to tell whether anything was left out
31
+ const loadedPlannedMessages = await listAllUserChatTimeouts({ limit: MAX_PLANNED_MESSAGE_MANAGER_ITEMS + 1 });
32
+ const hasMore = loadedPlannedMessages.length > MAX_PLANNED_MESSAGE_MANAGER_ITEMS;
33
+ const plannedMessages = hasMore
34
+ ? loadedPlannedMessages.slice(0, MAX_PLANNED_MESSAGE_MANAGER_ITEMS)
35
+ : loadedPlannedMessages;
36
+
37
+ const [usernamesById, agentNamesByPermanentId] = await Promise.all([
38
+ loadUsernamesByUserId(plannedMessages.map((plannedMessage) => plannedMessage.userId)),
39
+ loadAgentNamesByPermanentId(plannedMessages.map((plannedMessage) => plannedMessage.agentPermanentId)),
40
+ ]);
41
+ const generatedAtDate = new Date();
42
+
43
+ return {
44
+ items: plannedMessages.map((plannedMessage) =>
45
+ mapPlannedMessageManagerRecord({
46
+ plannedMessage,
47
+ usernamesById,
48
+ agentNamesByPermanentId,
49
+ atDate: generatedAtDate,
50
+ }),
51
+ ),
52
+ generatedAt: generatedAtDate.toISOString(),
53
+ hasMore,
54
+ };
55
+ }
@@ -0,0 +1,18 @@
1
+ import { PLANNED_MESSAGE_LIFECYCLES_BY_VIEW } from './filterPlannedMessages';
2
+ import type { PlannedMessageLifecycle } from './resolvePlannedMessageLifecycle';
3
+
4
+ /**
5
+ * Determines whether one planned message is still part of the agent's plans.
6
+ *
7
+ * A planned message is still planned while it can wake its agent again — including one held back by a
8
+ * pause and one whose starting date is still ahead. This is what decides whether it can be re-planned
9
+ * or cancelled at all, and it is the very same set the `active` view lists.
10
+ *
11
+ * @param lifecycle - Stage the planned message is in.
12
+ * @returns `true` while the planned message is not over.
13
+ *
14
+ * @private internal admin utility of Agents Server
15
+ */
16
+ export function isPlannedMessageStillPlanned(lifecycle: PlannedMessageLifecycle): boolean {
17
+ return PLANNED_MESSAGE_LIFECYCLES_BY_VIEW.active.includes(lifecycle);
18
+ }
@@ -0,0 +1,32 @@
1
+ import { loadAgentNamesByPermanentId, loadUsernamesByUserId } from '../adminEntityLookups';
2
+ import type { PlannedMessageManagerRecord } from '../plannedMessagesAdmin';
3
+ import { getUserChatTimeoutById } from '../userChatTimeout';
4
+ import { mapPlannedMessageManagerRecord } from './mapPlannedMessageManagerRecord';
5
+
6
+ /**
7
+ * Loads one planned message in the shape the admin manager shows it in.
8
+ *
9
+ * @param timeoutId - Id of the planned message.
10
+ * @returns Planned-message manager row, or `null` when there is no such planned message.
11
+ *
12
+ * @private internal admin utility of Agents Server
13
+ */
14
+ export async function loadPlannedMessageManagerRecord(timeoutId: string): Promise<PlannedMessageManagerRecord | null> {
15
+ const plannedMessage = await getUserChatTimeoutById(timeoutId);
16
+
17
+ if (!plannedMessage) {
18
+ return null;
19
+ }
20
+
21
+ const [usernamesById, agentNamesByPermanentId] = await Promise.all([
22
+ loadUsernamesByUserId([plannedMessage.userId]),
23
+ loadAgentNamesByPermanentId([plannedMessage.agentPermanentId]),
24
+ ]);
25
+
26
+ return mapPlannedMessageManagerRecord({
27
+ plannedMessage,
28
+ usernamesById,
29
+ agentNamesByPermanentId,
30
+ atDate: new Date(),
31
+ });
32
+ }
@@ -0,0 +1,73 @@
1
+ import type { PlannedMessageManagerRecord } from '../plannedMessagesAdmin';
2
+ import { isAgentGoalChatId } from '../agentGoalChat/agentGoalChatIdentity';
3
+ import type { UserChatTimeoutRecord } from '../userChatTimeout/UserChatTimeoutRecord';
4
+ import { resolvePlannedMessageEndReason } from './resolvePlannedMessageEndReason';
5
+ import { resolvePlannedMessageLifecycle } from './resolvePlannedMessageLifecycle';
6
+ import { resolvePlannedMessageRecurrenceKind } from './resolvePlannedMessageRecurrenceKind';
7
+
8
+ /**
9
+ * Everything needed to turn one stored timeout into a manager row.
10
+ *
11
+ * @private type of `mapPlannedMessageManagerRecord`
12
+ */
13
+ type MapPlannedMessageManagerRecordOptions = {
14
+ readonly plannedMessage: UserChatTimeoutRecord;
15
+ readonly usernamesById: ReadonlyMap<number, string>;
16
+ readonly agentNamesByPermanentId: ReadonlyMap<string, string | null>;
17
+
18
+ /**
19
+ * Moment the stage of the planned message is judged at.
20
+ */
21
+ readonly atDate: Date;
22
+ };
23
+
24
+ /**
25
+ * Maps one stored timeout into the row shown by the admin planned-message manager.
26
+ *
27
+ * The stage and the ending reason are derived here once, so every later step — filtering, counting,
28
+ * and rendering — reads the same answer instead of judging the raw columns again.
29
+ *
30
+ * @param options - Stored timeout with the names of its agent and owner.
31
+ * @returns Planned-message manager row.
32
+ *
33
+ * @private internal admin utility of Agents Server
34
+ */
35
+ export function mapPlannedMessageManagerRecord(
36
+ options: MapPlannedMessageManagerRecordOptions,
37
+ ): PlannedMessageManagerRecord {
38
+ const { plannedMessage, usernamesById, agentNamesByPermanentId, atDate } = options;
39
+ const lifecycle = resolvePlannedMessageLifecycle(plannedMessage, atDate);
40
+
41
+ return {
42
+ timeoutId: plannedMessage.timeoutId,
43
+ status: plannedMessage.status,
44
+ lifecycle,
45
+ recurrenceKind: resolvePlannedMessageRecurrenceKind(plannedMessage),
46
+ endReason: lifecycle === 'ENDED' ? resolvePlannedMessageEndReason(plannedMessage, atDate) : null,
47
+ message: plannedMessage.message,
48
+
49
+ createdAt: plannedMessage.createdAt,
50
+ updatedAt: plannedMessage.updatedAt,
51
+ dueAt: plannedMessage.dueAt,
52
+ startsAt: plannedMessage.startsAt,
53
+ endsAt: plannedMessage.endsAt,
54
+ lastFiredAt: plannedMessage.lastFiredAt,
55
+ completedAt: plannedMessage.completedAt,
56
+ cancelRequestedAt: plannedMessage.cancelRequestedAt,
57
+ pausedAt: plannedMessage.pausedAt,
58
+
59
+ recurrenceIntervalMs: plannedMessage.recurrenceIntervalMs,
60
+ cronExpression: plannedMessage.cronExpression,
61
+ maxRunCount: plannedMessage.maxRunCount,
62
+ runCount: plannedMessage.runCount,
63
+ attemptCount: plannedMessage.attemptCount,
64
+ failureReason: plannedMessage.failureReason,
65
+
66
+ userId: plannedMessage.userId,
67
+ username: usernamesById.get(plannedMessage.userId) ?? null,
68
+ agentPermanentId: plannedMessage.agentPermanentId,
69
+ agentName: agentNamesByPermanentId.get(plannedMessage.agentPermanentId) ?? null,
70
+ chatId: plannedMessage.chatId,
71
+ isAgentGoalChat: isAgentGoalChatId(plannedMessage.chatId),
72
+ };
73
+ }
@@ -0,0 +1,76 @@
1
+ import { spaceTrim } from 'spacetrim';
2
+ import { ParseError } from '../../../../../src/errors/ParseError';
3
+ import type { UpdateManagedPlannedMessageRequest } from './updateManagedPlannedMessage';
4
+
5
+ /**
6
+ * Fields one administrator request may carry, in the order they are read.
7
+ *
8
+ * @private constant of `parsePlannedMessageManagerUpdateRequest`
9
+ */
10
+ const PLANNED_MESSAGE_UPDATE_REQUEST_FIELDS = [
11
+ 'message',
12
+ 'milliseconds',
13
+ 'cronExpression',
14
+ 'startsAt',
15
+ 'endsAt',
16
+ 'maxRunCount',
17
+ ] as const satisfies ReadonlyArray<keyof UpdateManagedPlannedMessageRequest>;
18
+
19
+ /**
20
+ * Parses the untrusted body of one planned-message change request.
21
+ *
22
+ * Only the presence of each field is decided here — what a field is allowed to contain stays with the
23
+ * shared planned-message service, so an administrator and an agent are held to one set of rules.
24
+ *
25
+ * @param rawBody - Parsed JSON body of the request.
26
+ * @returns Requested change, carrying exactly the fields that were really sent.
27
+ * @throws {ParseError} When the body is not an object, carries no change, or holds an invalid `isPaused`.
28
+ *
29
+ * @private internal admin utility of Agents Server
30
+ */
31
+ export function parsePlannedMessageManagerUpdateRequest(rawBody: unknown): UpdateManagedPlannedMessageRequest {
32
+ if (typeof rawBody !== 'object' || rawBody === null || Array.isArray(rawBody)) {
33
+ throw new ParseError(
34
+ spaceTrim(`
35
+ Invalid planned-message change.
36
+
37
+ - The request body must be a **JSON object** describing the change.
38
+ `),
39
+ );
40
+ }
41
+
42
+ const requestBody = rawBody as Record<string, unknown>;
43
+ const parsedRequest: Record<string, unknown> = {};
44
+
45
+ for (const fieldName of PLANNED_MESSAGE_UPDATE_REQUEST_FIELDS) {
46
+ if (Object.prototype.hasOwnProperty.call(requestBody, fieldName)) {
47
+ parsedRequest[fieldName] = requestBody[fieldName];
48
+ }
49
+ }
50
+
51
+ if (Object.prototype.hasOwnProperty.call(requestBody, 'isPaused')) {
52
+ if (typeof requestBody.isPaused !== 'boolean') {
53
+ throw new ParseError(
54
+ spaceTrim(`
55
+ Invalid planned-message change.
56
+
57
+ - \`isPaused\` must be \`true\` to hold the planned message back, or \`false\` to let it go again.
58
+ `),
59
+ );
60
+ }
61
+
62
+ parsedRequest.isPaused = requestBody.isPaused;
63
+ }
64
+
65
+ if (Object.keys(parsedRequest).length === 0) {
66
+ throw new ParseError(
67
+ spaceTrim(`
68
+ Nothing to change in this planned message.
69
+
70
+ - Pass at least one of \`cronExpression\`, \`milliseconds\`, \`startsAt\`, \`endsAt\`, \`maxRunCount\`, \`message\`, or \`isPaused\`.
71
+ `),
72
+ );
73
+ }
74
+
75
+ return parsedRequest as UpdateManagedPlannedMessageRequest;
76
+ }
@@ -0,0 +1,59 @@
1
+ import { resolvePlannedMessageRecurrenceKind } from './resolvePlannedMessageRecurrenceKind';
2
+
3
+ /**
4
+ * Why one planned message stopped waking its agent.
5
+ *
6
+ * `null` means the planned message is not over, or that nothing in its own schedule explains the end
7
+ * — a cancelled or failed message ends for a reason outside its schedule.
8
+ *
9
+ * @private internal admin utility of Agents Server
10
+ */
11
+ export type PlannedMessageEndReason = 'MAX_RUNS_REACHED' | 'ENDING_DATE_PASSED' | 'SINGLE_RUN_DONE' | null;
12
+
13
+ /**
14
+ * Stored fields deciding why one planned message is over.
15
+ *
16
+ * @private internal admin utility of Agents Server
17
+ */
18
+ export type PlannedMessageEndReasonInput = {
19
+ readonly recurrenceIntervalMs: number | null;
20
+ readonly cronExpression: string | null;
21
+ readonly endsAt: string | null;
22
+ readonly maxRunCount: number | null;
23
+ readonly runCount: number;
24
+ };
25
+
26
+ /**
27
+ * Resolves why one finished planned message will never wake its agent again.
28
+ *
29
+ * The bounds are read in the same order the scheduler applies them, so the manager explains an ended
30
+ * plan exactly the way it really ended.
31
+ *
32
+ * @param plannedMessage - Stored planned message that is already over.
33
+ * @param atDate - Moment the ending is judged at.
34
+ * @returns Reason the plan is over, or `null` when its schedule does not explain the end.
35
+ *
36
+ * @private internal admin utility of Agents Server
37
+ */
38
+ export function resolvePlannedMessageEndReason(
39
+ plannedMessage: PlannedMessageEndReasonInput,
40
+ atDate: Date,
41
+ ): PlannedMessageEndReason {
42
+ if (plannedMessage.maxRunCount !== null && plannedMessage.runCount >= plannedMessage.maxRunCount) {
43
+ return 'MAX_RUNS_REACHED';
44
+ }
45
+
46
+ if (plannedMessage.endsAt !== null) {
47
+ const endsAtTime = Date.parse(plannedMessage.endsAt);
48
+
49
+ if (Number.isFinite(endsAtTime) && endsAtTime <= atDate.getTime()) {
50
+ return 'ENDING_DATE_PASSED';
51
+ }
52
+ }
53
+
54
+ if (resolvePlannedMessageRecurrenceKind(plannedMessage) === 'ONCE') {
55
+ return 'SINGLE_RUN_DONE';
56
+ }
57
+
58
+ return null;
59
+ }