@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
package/esm/index.es.js CHANGED
@@ -4,7 +4,7 @@ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
4
  import { writeFile, stat, mkdir, readFile, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, access, constants, watch, rmdir } from 'fs/promises';
5
5
  import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
6
6
  import { createHash, randomBytes } from 'crypto';
7
- import { spawn, spawnSync } from 'child_process';
7
+ import { spawn, spawnSync, execFile } from 'child_process';
8
8
  import moment from 'moment';
9
9
  import * as fs from 'fs';
10
10
  import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-13';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-18';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2207,7 +2207,7 @@ async function isExistingFile$4(path) {
2207
2207
  */
2208
2208
  async function ensureDirectory(projectPath, relativeDirectoryPath) {
2209
2209
  const directoryPath = join(projectPath, relativeDirectoryPath);
2210
- const isDirectoryExisting = await isExistingDirectory$1(directoryPath);
2210
+ const isDirectoryExisting = await isExistingDirectory$2(directoryPath);
2211
2211
  if (!isDirectoryExisting) {
2212
2212
  await mkdir(directoryPath, { recursive: true });
2213
2213
  return 'created';
@@ -2217,7 +2217,7 @@ async function ensureDirectory(projectPath, relativeDirectoryPath) {
2217
2217
  /**
2218
2218
  * Checks whether a path exists and is a directory.
2219
2219
  */
2220
- async function isExistingDirectory$1(path) {
2220
+ async function isExistingDirectory$2(path) {
2221
2221
  try {
2222
2222
  return (await stat(path)).isDirectory();
2223
2223
  }
@@ -5716,6 +5716,12 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
5716
5716
 
5717
5717
  Inherits agent source from another agent.
5718
5718
 
5719
+ Every agent inherits from the core agent \`@Adam\` unless it says otherwise, so writing no ${this.type} at all
5720
+ is the same as writing \`${this.type} @Adam\`. Write \`${this.type} @Null\` to inherit from nothing.
5721
+
5722
+ An agent has exactly one parent, so when ${this.type} is written more than once, the last one wins and
5723
+ overrides every earlier one.
5724
+
5719
5725
  ## Examples
5720
5726
 
5721
5727
  \`\`\`book
@@ -5724,6 +5730,13 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
5724
5730
  FROM https://s6.ptbk.io/benjamin-white
5725
5731
  RULE Speak only in English.
5726
5732
  \`\`\`
5733
+
5734
+ \`\`\`book
5735
+ My Standalone Agent
5736
+
5737
+ FROM @Null
5738
+ RULE Speak only in English.
5739
+ \`\`\`
5727
5740
  `);
5728
5741
  }
5729
5742
  applyToAgentModelRequirements(requirements, content) {
@@ -30572,6 +30585,385 @@ function buildPromptTestScriptPath(scriptPath) {
30572
30585
  return `${scriptPath}.test.sh`;
30573
30586
  }
30574
30587
 
30588
+ /**
30589
+ * Lists the direct project directories of one agent folder.
30590
+ *
30591
+ * @param agentFolderPath - Absolute path of the local agent folder.
30592
+ * @returns Project directory names, or an empty list when the agent has no projects folder.
30593
+ */
30594
+ async function listAgentProjectDirectoryNames(agentFolderPath) {
30595
+ try {
30596
+ const projectsRootEntries = await readdir(join(agentFolderPath, AGENT_PROJECTS_DIRECTORY_PATH), {
30597
+ withFileTypes: true,
30598
+ });
30599
+ return projectsRootEntries
30600
+ .filter((projectsRootEntry) => projectsRootEntry.isDirectory())
30601
+ .map((projectDirectoryEntry) => projectDirectoryEntry.name);
30602
+ }
30603
+ catch (_a) {
30604
+ return [];
30605
+ }
30606
+ }
30607
+
30608
+ /**
30609
+ * Largest git output kept in memory, so an enormous diff can never exhaust the runner process.
30610
+ */
30611
+ const AGENT_PROJECT_GIT_OUTPUT_MAX_BYTES = 8 * 1024 * 1024;
30612
+ /**
30613
+ * Time after which one git command of a project repository is given up on.
30614
+ */
30615
+ const AGENT_PROJECT_GIT_TIMEOUT_MS = 60 * 1000;
30616
+ /**
30617
+ * Author and committer used for project commits when no coding-agent identity is configured.
30618
+ *
30619
+ * A project repository is created by the runner on a server which usually has no git identity at
30620
+ * all, so committing must never depend on the machine configuration.
30621
+ */
30622
+ const AGENT_PROJECT_GIT_FALLBACK_IDENTITY = {
30623
+ GIT_AUTHOR_NAME: 'Promptbook Agent',
30624
+ GIT_AUTHOR_EMAIL: 'agent@promptbook.studio',
30625
+ GIT_COMMITTER_NAME: 'Promptbook Agent',
30626
+ GIT_COMMITTER_EMAIL: 'agent@promptbook.studio',
30627
+ };
30628
+ /**
30629
+ * Runs one git command inside one agent project directory.
30630
+ *
30631
+ * Commands are executed without a shell and with an explicit argument list, so a project name or
30632
+ * a file path can never be interpreted as a shell fragment. A failing command is reported through
30633
+ * `isSuccessful` instead of throwing, because git uses the exit code to answer questions such as
30634
+ * "is there anything staged" and because auto-committing a project must never fail an answer the
30635
+ * agent already produced.
30636
+ *
30637
+ * @param options - Project directory and the git arguments to run there.
30638
+ * @returns Outcome of the command.
30639
+ */
30640
+ async function runAgentProjectGitCommand(options) {
30641
+ return await new Promise((resolve) => {
30642
+ execFile('git', [...options.args], {
30643
+ cwd: options.projectPath,
30644
+ env: { ...process.env, ...AGENT_PROJECT_GIT_FALLBACK_IDENTITY, ...(buildAgentGitEnv() || {}) },
30645
+ maxBuffer: AGENT_PROJECT_GIT_OUTPUT_MAX_BYTES,
30646
+ timeout: AGENT_PROJECT_GIT_TIMEOUT_MS,
30647
+ windowsHide: true,
30648
+ }, (error, stdout) => {
30649
+ resolve({
30650
+ isSuccessful: !error,
30651
+ output: stripTrailingNewline(stdout || ''),
30652
+ });
30653
+ });
30654
+ });
30655
+ }
30656
+ /**
30657
+ * Removes the single trailing newline git appends to its output.
30658
+ *
30659
+ * @param output - Raw command output.
30660
+ * @returns Output without its trailing newline.
30661
+ *
30662
+ * @private helper of `runAgentProjectGitCommand`
30663
+ */
30664
+ function stripTrailingNewline(output) {
30665
+ return output.replace(/\r?\n$/u, '');
30666
+ }
30667
+
30668
+ /**
30669
+ * Branch created for a project repository, so every agent project starts with the same history.
30670
+ */
30671
+ const AGENT_PROJECT_GIT_INITIAL_BRANCH = 'main';
30672
+ /**
30673
+ * Paths kept out of a project repository created by the runner.
30674
+ *
30675
+ * Only a project which brings no ignore rules of its own receives these, and they cover exactly
30676
+ * the two things that must never enter an automatic commit: installed dependencies and secrets.
30677
+ */
30678
+ const AGENT_PROJECT_GIT_DEFAULT_IGNORE_PATHS = spaceTrim$1(`
30679
+ node_modules/
30680
+ .env
30681
+ .env.*
30682
+ `);
30683
+ /**
30684
+ * Makes sure one agent project is a git repository of its own.
30685
+ *
30686
+ * A project which is already tracked by a repository around the agent folder is deliberately left
30687
+ * alone: initializing a repository inside it would turn it into an embedded repository and break
30688
+ * the history that already covers it.
30689
+ *
30690
+ * @param projectPath - Absolute path of the project directory.
30691
+ * @returns How the project relates to git after this call.
30692
+ */
30693
+ async function ensureAgentProjectGitRepository(projectPath) {
30694
+ if (await isExistingDirectory$1(join(projectPath, '.git'))) {
30695
+ return 'own-repository';
30696
+ }
30697
+ if (await isProjectTrackedByOuterRepository(projectPath)) {
30698
+ return 'tracked-elsewhere';
30699
+ }
30700
+ if (!(await initializeAgentProjectGitRepository(projectPath))) {
30701
+ return 'unavailable';
30702
+ }
30703
+ await writeDefaultAgentProjectGitignoreIfMissing(projectPath);
30704
+ return 'own-repository';
30705
+ }
30706
+ /**
30707
+ * Creates the repository of one project, naming its first branch when git supports it.
30708
+ *
30709
+ * @param projectPath - Absolute path of the project directory.
30710
+ * @returns `true` when the project is a repository afterwards.
30711
+ *
30712
+ * @private helper of `ensureAgentProjectGitRepository`
30713
+ */
30714
+ async function initializeAgentProjectGitRepository(projectPath) {
30715
+ const initResult = await runAgentProjectGitCommand({
30716
+ projectPath,
30717
+ args: ['init', `--initial-branch=${AGENT_PROJECT_GIT_INITIAL_BRANCH}`],
30718
+ });
30719
+ if (initResult.isSuccessful) {
30720
+ return true;
30721
+ }
30722
+ // Note: `--initial-branch` needs git 2.28, so an older git still gets its repository
30723
+ return (await runAgentProjectGitCommand({ projectPath, args: ['init'] })).isSuccessful;
30724
+ }
30725
+ /**
30726
+ * Checks whether a repository around the agent folder already tracks the project files.
30727
+ *
30728
+ * @param projectPath - Absolute path of the project directory.
30729
+ * @returns `true` when an outer repository tracks at least one file of the project.
30730
+ *
30731
+ * @private helper of `ensureAgentProjectGitRepository`
30732
+ */
30733
+ async function isProjectTrackedByOuterRepository(projectPath) {
30734
+ const trackedFilesResult = await runAgentProjectGitCommand({
30735
+ projectPath,
30736
+ args: ['ls-files', '--', '.'],
30737
+ });
30738
+ return trackedFilesResult.isSuccessful && trackedFilesResult.output.trim().length > 0;
30739
+ }
30740
+ /**
30741
+ * Writes the default ignore rules into a freshly initialized project repository.
30742
+ *
30743
+ * A project which already brings its own rules keeps them untouched.
30744
+ *
30745
+ * @param projectPath - Absolute path of the project directory.
30746
+ *
30747
+ * @private helper of `ensureAgentProjectGitRepository`
30748
+ */
30749
+ async function writeDefaultAgentProjectGitignoreIfMissing(projectPath) {
30750
+ const gitignorePath = join(projectPath, '.gitignore');
30751
+ try {
30752
+ await stat(gitignorePath);
30753
+ return;
30754
+ }
30755
+ catch (_a) {
30756
+ // Note: A project without ignore rules would commit its installed dependencies
30757
+ }
30758
+ await writeFile(gitignorePath, `${AGENT_PROJECT_GIT_DEFAULT_IGNORE_PATHS}\n`, 'utf-8');
30759
+ }
30760
+ /**
30761
+ * Checks whether one path is an existing directory.
30762
+ *
30763
+ * @param directoryPath - Absolute path to check.
30764
+ * @returns `true` when the path exists and is a directory.
30765
+ *
30766
+ * @private helper of `ensureAgentProjectGitRepository`
30767
+ */
30768
+ async function isExistingDirectory$1(directoryPath) {
30769
+ try {
30770
+ return (await stat(directoryPath)).isDirectory();
30771
+ }
30772
+ catch (_a) {
30773
+ return false;
30774
+ }
30775
+ }
30776
+
30777
+ /**
30778
+ * Largest diff carried into a chat, so one huge commit cannot bloat every message payload.
30779
+ */
30780
+ const AGENT_PROJECT_DIFF_MAX_LENGTH = 20000;
30781
+ /**
30782
+ * Describes the latest commit of one agent project repository.
30783
+ *
30784
+ * @param options - Project directory and the project name the description is reported under.
30785
+ * @returns Description of the commit, or `null` when git could not describe it.
30786
+ */
30787
+ async function readAgentProjectCommitChange(options) {
30788
+ const { projectPath, projectName } = options;
30789
+ const [commitHashResult, committedAtResult, numstatResult, diffResult] = await Promise.all([
30790
+ runAgentProjectGitCommand({ projectPath, args: ['rev-parse', 'HEAD'] }),
30791
+ runAgentProjectGitCommand({ projectPath, args: ['show', '--no-patch', '--format=%cI', 'HEAD'] }),
30792
+ runAgentProjectGitCommand({ projectPath, args: ['show', '--numstat', '--format=', 'HEAD'] }),
30793
+ runAgentProjectGitCommand({ projectPath, args: ['show', '--patch', '--format=', 'HEAD'] }),
30794
+ ]);
30795
+ if (!commitHashResult.isSuccessful || commitHashResult.output.trim().length === 0) {
30796
+ return null;
30797
+ }
30798
+ const changedFiles = parseAgentProjectNumstat(numstatResult.isSuccessful ? numstatResult.output : '');
30799
+ const diff = diffResult.isSuccessful ? diffResult.output : '';
30800
+ return {
30801
+ projectName,
30802
+ commitHash: commitHashResult.output.trim(),
30803
+ committedAt: resolveCommittedAt(committedAtResult.isSuccessful ? committedAtResult.output : ''),
30804
+ changedFiles,
30805
+ insertionCount: sumChangedFileCounts(changedFiles, 'insertionCount'),
30806
+ deletionCount: sumChangedFileCounts(changedFiles, 'deletionCount'),
30807
+ diff: diff.slice(0, AGENT_PROJECT_DIFF_MAX_LENGTH),
30808
+ isDiffTruncated: diff.length > AGENT_PROJECT_DIFF_MAX_LENGTH,
30809
+ };
30810
+ }
30811
+ /**
30812
+ * Parses the `git show --numstat` output into changed files.
30813
+ *
30814
+ * Binary files report `-` instead of line counts, which is kept as a zero-line change so the file
30815
+ * is still listed as touched.
30816
+ *
30817
+ * @param numstatOutput - Raw numstat output.
30818
+ * @returns Changed files in the order git reported them.
30819
+ *
30820
+ * @private helper of `readAgentProjectCommitChange`
30821
+ */
30822
+ function parseAgentProjectNumstat(numstatOutput) {
30823
+ return numstatOutput
30824
+ .split(/\r?\n/u)
30825
+ .map((numstatLine) => numstatLine.split('\t'))
30826
+ .filter((numstatColumns) => numstatColumns.length >= 3 && numstatColumns[2].trim().length > 0)
30827
+ .map((numstatColumns) => ({
30828
+ path: numstatColumns.slice(2).join('\t').trim(),
30829
+ insertionCount: parseChangedLineCount(numstatColumns[0]),
30830
+ deletionCount: parseChangedLineCount(numstatColumns[1]),
30831
+ }));
30832
+ }
30833
+ /**
30834
+ * Parses one numstat line count.
30835
+ *
30836
+ * @param rawLineCount - Raw numstat column.
30837
+ * @returns Parsed count, or `0` for binary files and unparseable columns.
30838
+ *
30839
+ * @private helper of `readAgentProjectCommitChange`
30840
+ */
30841
+ function parseChangedLineCount(rawLineCount) {
30842
+ const parsedLineCount = Number.parseInt(rawLineCount.trim(), 10);
30843
+ return Number.isFinite(parsedLineCount) && parsedLineCount >= 0 ? parsedLineCount : 0;
30844
+ }
30845
+ /**
30846
+ * Sums one line-count field across all changed files.
30847
+ *
30848
+ * @param changedFiles - Changed files of the commit.
30849
+ * @param countName - Field to sum.
30850
+ * @returns Total count.
30851
+ *
30852
+ * @private helper of `readAgentProjectCommitChange`
30853
+ */
30854
+ function sumChangedFileCounts(changedFiles, countName) {
30855
+ return changedFiles.reduce((total, changedFile) => total + changedFile[countName], 0);
30856
+ }
30857
+ /**
30858
+ * Normalizes the commit timestamp git reported.
30859
+ *
30860
+ * @param rawCommittedAt - Raw ISO timestamp from git.
30861
+ * @returns Usable ISO timestamp, falling back to the current time when git reported none.
30862
+ *
30863
+ * @private helper of `readAgentProjectCommitChange`
30864
+ */
30865
+ function resolveCommittedAt(rawCommittedAt) {
30866
+ const trimmedCommittedAt = rawCommittedAt.trim();
30867
+ if (trimmedCommittedAt.length === 0 || !Number.isFinite(Date.parse(trimmedCommittedAt))) {
30868
+ return new Date().toISOString();
30869
+ }
30870
+ return new Date(trimmedCommittedAt).toISOString();
30871
+ }
30872
+
30873
+ /**
30874
+ * Commits everything one answered message changed inside one agent project.
30875
+ *
30876
+ * The project is turned into a git repository first when it is not one yet, so the work of every
30877
+ * answer stays recoverable and reviewable as a normal commit history.
30878
+ *
30879
+ * @param options - Project identity and the commit message describing the answered message.
30880
+ * @returns What was committed, or `null` when the project changed nothing or cannot be committed.
30881
+ */
30882
+ async function commitAgentProjectChanges(options) {
30883
+ const { projectPath, projectName, commitMessage } = options;
30884
+ if ((await ensureAgentProjectGitRepository(projectPath)) !== 'own-repository') {
30885
+ return null;
30886
+ }
30887
+ const stageResult = await runAgentProjectGitCommand({ projectPath, args: ['add', '--all', '--', '.'] });
30888
+ if (!stageResult.isSuccessful) {
30889
+ return null;
30890
+ }
30891
+ if (!(await hasStagedAgentProjectChanges(projectPath))) {
30892
+ return null;
30893
+ }
30894
+ const commitResult = await runAgentProjectGitCommand({
30895
+ projectPath,
30896
+ args: ['commit', '--message', commitMessage],
30897
+ });
30898
+ if (!commitResult.isSuccessful) {
30899
+ return null;
30900
+ }
30901
+ return await readAgentProjectCommitChange({ projectPath, projectName });
30902
+ }
30903
+ /**
30904
+ * Checks whether the project index holds anything to commit.
30905
+ *
30906
+ * @param projectPath - Absolute path of the project directory.
30907
+ * @returns `true` when committing would create a non-empty commit.
30908
+ *
30909
+ * @private helper of `commitAgentProjectChanges`
30910
+ */
30911
+ async function hasStagedAgentProjectChanges(projectPath) {
30912
+ // Note: `--quiet` makes git answer through its exit code — success means nothing is staged
30913
+ const stagedChangesResult = await runAgentProjectGitCommand({
30914
+ projectPath,
30915
+ args: ['diff', '--cached', '--quiet'],
30916
+ });
30917
+ return !stagedChangesResult.isSuccessful;
30918
+ }
30919
+
30920
+ /**
30921
+ * Commits what one answered message changed in every project of one agent.
30922
+ *
30923
+ * Every project of an agent is its own git repository, so each answer which modified one becomes a
30924
+ * commit there. The projects are committed one after another because they share the git executable
30925
+ * and because an agent works with a handful of projects at most.
30926
+ *
30927
+ * Committing is best-effort bookkeeping around an answer the agent already produced: a project
30928
+ * whose commit fails is reported to the console and skipped instead of failing the message.
30929
+ *
30930
+ * @param options - Agent folder and the commit message describing the answered message.
30931
+ * @returns What was committed, one entry per project the message really changed.
30932
+ */
30933
+ async function commitAnsweredMessageProjectChanges(options) {
30934
+ const { agentFolderPath, commitMessage } = options;
30935
+ const projectNames = await listAgentProjectDirectoryNames(agentFolderPath);
30936
+ const projectChanges = [];
30937
+ for (const projectName of projectNames) {
30938
+ const projectChange = await commitAgentProjectChangesSafely({
30939
+ projectPath: join(agentFolderPath, AGENT_PROJECTS_DIRECTORY_PATH, projectName),
30940
+ projectName,
30941
+ commitMessage,
30942
+ });
30943
+ if (projectChange) {
30944
+ projectChanges.push(projectChange);
30945
+ }
30946
+ }
30947
+ return projectChanges;
30948
+ }
30949
+ /**
30950
+ * Commits one project without letting its failure reach the answered message.
30951
+ *
30952
+ * @param options - Project identity and commit message.
30953
+ * @returns What was committed, or `null` when nothing changed or committing failed.
30954
+ *
30955
+ * @private helper of `commitAnsweredMessageProjectChanges`
30956
+ */
30957
+ async function commitAgentProjectChangesSafely(options) {
30958
+ try {
30959
+ return await commitAgentProjectChanges(options);
30960
+ }
30961
+ catch (error) {
30962
+ console.warn(colors.yellow(`Could not commit changes of project "${options.projectName}"`), error);
30963
+ return null;
30964
+ }
30965
+ }
30966
+
30575
30967
  /**
30576
30968
  * Checks whether one repository-relative path is tracked by Git.
30577
30969
  */
@@ -30617,7 +31009,7 @@ function buildAgentProjectsPromptSection(options = {}) {
30617
31009
 
30618
31010
  - You have 100% control over every project folder: create, read, modify, and delete files there, run scripts and commands inside it, and keep any persistent data you need between conversations.
30619
31011
  - When the user asks you to build something (for example a website) or to do longer-term work, create a new project folder \`${AGENT_PROJECTS_DIRECTORY_PATH}/<project-name>/\` (use a short kebab-case name) and do the work inside it. Modify an existing project when the user refers to work you already did.
30620
- - A project can be a git repository you can run \`git init\` inside a project folder and commit your work there.
31012
+ - Every project is a git repository and everything you change in one is committed there automatically once you finish the message, so you never need to run \`git init\` or \`git commit\` yourself and your work stays recoverable.
30621
31013
  - Do not touch anything outside the \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory beyond the file changes allowed at the top of this prompt.
30622
31014
 
30623
31015
  ${block(buildProjectReferenceInstructions(projectsUrlPath))}
@@ -30749,13 +31141,79 @@ function formatTimeoutUnit(value, unit) {
30749
31141
  }
30750
31142
  // Note: [💞] Ignore a discrepancy between file name and entity name
30751
31143
 
31144
+ /**
31145
+ * Describes one planned-message schedule in a single sentence fragment.
31146
+ *
31147
+ * A planned message can repeat forever, repeat a bounded number of times, run inside a date window, or
31148
+ * wake the agent only once. Every surface showing a planned message — the wake-up itself, the goal-chat
31149
+ * notes, the planned-message prompt section, and the model tools — describes it with this one helper,
31150
+ * so an agent never has to reconcile two different wordings of the same plan.
31151
+ *
31152
+ * @param schedule - Schedule of the planned message.
31153
+ * @returns Fragment such as `repeats every 5 minutes (2 of 10 runs done)`.
31154
+ *
31155
+ * @private internal utility of the Agents Server planned messages
31156
+ */
31157
+ function describeAgentPlannedMessageSchedule(schedule) {
31158
+ const scheduleParts = [
31159
+ describeAgentPlannedMessageRecurrence(schedule),
31160
+ ...describeAgentPlannedMessageBounds(schedule),
31161
+ ];
31162
+ return scheduleParts.join(', ');
31163
+ }
31164
+ /**
31165
+ * Describes how often one planned message wakes the agent.
31166
+ *
31167
+ * @param schedule - Schedule of the planned message.
31168
+ * @returns Recurrence fragment.
31169
+ *
31170
+ * @private internal utility of `describeAgentPlannedMessageSchedule`
31171
+ */
31172
+ function describeAgentPlannedMessageRecurrence(schedule) {
31173
+ if (schedule.cronExpression) {
31174
+ return `repeats on cron \`${schedule.cronExpression}\``;
31175
+ }
31176
+ if (schedule.intervalMs) {
31177
+ return `repeats every ${formatTimeoutDurationHuman(schedule.intervalMs)}`;
31178
+ }
31179
+ return schedule.dueAt ? `wakes you once at ${schedule.dueAt}` : 'wakes you once';
31180
+ }
31181
+ /**
31182
+ * Describes the bounds limiting how long one planned message keeps repeating.
31183
+ *
31184
+ * @param schedule - Schedule of the planned message.
31185
+ * @returns Fragments for the starting date, ending date, and run count that are really set.
31186
+ *
31187
+ * @private internal utility of `describeAgentPlannedMessageSchedule`
31188
+ */
31189
+ function describeAgentPlannedMessageBounds(schedule) {
31190
+ const bounds = [];
31191
+ const runCount = schedule.runCount || 0;
31192
+ if (schedule.startsAt) {
31193
+ bounds.push(`starting ${schedule.startsAt}`);
31194
+ }
31195
+ if (schedule.endsAt) {
31196
+ bounds.push(`until ${schedule.endsAt}`);
31197
+ }
31198
+ if (schedule.maxRunCount) {
31199
+ bounds.push(`${runCount} of ${schedule.maxRunCount} runs done`);
31200
+ }
31201
+ else if (runCount > 0) {
31202
+ bounds.push(`${runCount} run${runCount === 1 ? '' : 's'} done`);
31203
+ }
31204
+ if ((schedule.cronExpression || schedule.intervalMs) && schedule.dueAt) {
31205
+ bounds.push(`next at ${schedule.dueAt}`);
31206
+ }
31207
+ return bounds;
31208
+ }
31209
+
30752
31210
  /**
30753
31211
  * Builds the planned-message instructions available to an Agents Server-managed coding agent.
30754
31212
  *
30755
31213
  * Planned messages always target the agent's singleton goal chat, regardless of the chat in which the
30756
- * wake-up is planned, and they repeat like `setInterval` until they are cancelled. The sidecar file is
30757
- * the only channel that changes them, so an answer that merely claims a follow-up was planned changes
30758
- * nothing — and an answer that changes nothing keeps the current plan running.
31214
+ * wake-up is planned, and each of them keeps repeating until its schedule is over or it is cancelled.
31215
+ * The sidecar file is the only channel that changes them, so an answer that merely claims a follow-up
31216
+ * was planned changes nothing — and an answer that changes nothing keeps the current plan running.
30759
31217
  *
30760
31218
  * @param sidecar - Sidecar prepared by the Agents Server for the answered message.
30761
31219
  * @returns Prompt section, or an empty string outside Agents Server-managed runs.
@@ -30767,19 +31225,25 @@ function buildAgentGoalChatPromptSection(sidecar) {
30767
31225
  return spaceTrim$1((block) => `
30768
31226
  ## Planned goal-chat messages
30769
31227
 
30770
- You can plan a message that repeatedly wakes you in your singleton goal chat. A planned message works like \`setInterval\`, not like \`setTimeout\`: once planned, it keeps waking you at its interval until you cancel it. This works during every Agents Server-managed chat invocation, not only while answering inside the goal chat.
31228
+ You can plan a message that wakes you in your singleton goal chat. A planned message works like \`setInterval\`, not like \`setTimeout\`: once planned, it keeps waking you until its schedule is over or you cancel it. This works during every Agents Server-managed chat invocation, not only while answering inside the goal chat.
30771
31229
 
30772
31230
  ${block(buildCurrentPlannedMessagesLines(sidecar.currentPlannedMessages))}
30773
31231
 
30774
31232
  Editing \`${sidecar.relativeSidecarPath}\` is the **only** way to change what wakes you. Writing in your answer that you scheduled something does not schedule anything.
30775
31233
 
30776
- - **Keeping your planned messages as they are is the default.** When the list above already matches your goal, leave \`commands\` empty — every listed message keeps repeating on its own, and re-planning it would only duplicate it.
30777
- - When your goal needs a repeating wake-up that is not listed above, append \`{"action":"set","milliseconds":<repeat interval>,"message":"<what each future invocation must do>"}\` to the \`commands\` array of that file.
30778
- - When a listed message no longer matches your goal (for example your goal now says every 10 minutes while the message repeats every 5 minutes), append \`{"action":"cancel","timeoutId":"<timeout id>"}\` and one \`set\` command with the interval your goal really requires.
30779
- - When your goal no longer needs a listed message at all, cancel it without planning a replacement.
31234
+ - **Keeping your planned messages as they are is the default.** When the list above already matches your goal, leave \`commands\` empty — every listed message keeps waking you on its own, and re-planning it would only duplicate it.
31235
+ - When your goal needs a wake-up that is not listed above, append \`{"action":"set","milliseconds":<repeat interval>,"message":"<what each future invocation must do>"}\` to the \`commands\` array of that file.
31236
+ - A planned message is shaped by these fields, which \`set\` and \`update\` both accept:
31237
+ - \`milliseconds\` repeat interval of at least \`60000\`, or
31238
+ - \`cronExpression\` — a five-field cron such as \`"0 9 * * 1-5"\`, evaluated in the server time zone (use **either** this **or** \`milliseconds\`),
31239
+ - \`maxRunCount\` — how many times in total the message wakes you, for example \`1\` for a one-off message,
31240
+ - \`startsAt\` and \`endsAt\` — ISO dates bounding when it may wake you, for example \`"2026-09-01T08:00:00.000Z"\`.
31241
+ - When a listed message no longer matches your goal, append \`{"action":"update","timeoutId":"<timeout id>","<field>":<new value>}\` with only the fields you want to change. The message keeps its id, a field you leave out stays as it is, and \`null\` removes a bound such as \`endsAt\`. Use \`update\` instead of cancelling and planning the same message again.
31242
+ - When your goal no longer needs a listed message at all, append \`{"action":"cancel","timeoutId":"<timeout id>"}\` without planning a replacement.
30780
31243
  - Never edit \`version\`, \`agentPermanentId\`, or \`currentPlannedMessages\`.
30781
- - The Agents Server applies every command once your answer is finished, and each repetition appears in the goal chat and invokes you there.
30782
- - Only mention a planned follow-up in your answer when it is really planned: either you appended the matching \`set\` command, or you kept one of the messages listed above.
31244
+ - The Agents Server applies every command once your answer is finished, and each wake-up appears in the goal chat and invokes you there.
31245
+ - A planned message which finished because it ran \`maxRunCount\` times or passed its \`endsAt\` — stops on its own and disappears from the list above, so you never have to clean it up.
31246
+ - Only mention a planned follow-up in your answer when it is really planned: either you appended the matching \`set\` or \`update\` command, or you kept one of the messages listed above.
30783
31247
  `);
30784
31248
  }
30785
31249
  /**
@@ -30812,10 +31276,7 @@ function buildCurrentPlannedMessagesLines(currentPlannedMessages) {
30812
31276
  function createCurrentPlannedMessageLine(plannedMessage) {
30813
31277
  var _a;
30814
31278
  const message = ((_a = plannedMessage.message) === null || _a === void 0 ? void 0 : _a.trim()) || 'Continue working towards the current goal.';
30815
- const schedule = plannedMessage.intervalMs
30816
- ? `repeats every ${formatTimeoutDurationHuman(plannedMessage.intervalMs)}`
30817
- : `wakes you once at ${plannedMessage.dueAt}`;
30818
- return `- \`${plannedMessage.timeoutId}\` ${schedule}: ${message}`;
31279
+ return `- \`${plannedMessage.timeoutId}\` ${describeAgentPlannedMessageSchedule(plannedMessage)}: ${message}`;
30819
31280
  }
30820
31281
 
30821
31282
  /**
@@ -37875,7 +38336,7 @@ const AGENT_PLANNED_MESSAGES_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH
37875
38336
  *
37876
38337
  * @private internal convention shared by the Agents Server and agent-folder runner
37877
38338
  */
37878
- const AGENT_PLANNED_MESSAGE_COMMAND_ACTIONS = ['set', 'cancel'];
38339
+ const AGENT_PLANNED_MESSAGE_COMMAND_ACTIONS = ['set', 'update', 'cancel'];
37879
38340
  /**
37880
38341
  * Creates the relative planned-message sidecar path of one queued message.
37881
38342
  *
@@ -37967,19 +38428,35 @@ function normalizeAgentPlannedMessageSnapshots(rawSnapshots) {
37967
38428
  * Normalizes the optional fields of one already planned message.
37968
38429
  *
37969
38430
  * @param snapshot - Planned-message entry with the required identity fields.
37970
- * @returns Snapshot with a usable repeat interval and message.
38431
+ * @returns Snapshot with a usable schedule and message.
37971
38432
  *
37972
38433
  * @private internal utility of `parseAgentPlannedMessagesSidecar`
37973
38434
  */
37974
38435
  function createNormalizedAgentPlannedMessageSnapshot(snapshot) {
37975
- const intervalMs = Number(snapshot.intervalMs);
37976
38436
  return {
37977
38437
  timeoutId: snapshot.timeoutId,
37978
38438
  dueAt: snapshot.dueAt,
37979
38439
  message: typeof snapshot.message === 'string' ? snapshot.message : null,
37980
- intervalMs: Number.isFinite(intervalMs) && intervalMs > 0 ? Math.floor(intervalMs) : null,
38440
+ intervalMs: normalizeAgentPlannedMessagePositiveCount(snapshot.intervalMs),
38441
+ cronExpression: typeof snapshot.cronExpression === 'string' ? snapshot.cronExpression : null,
38442
+ startsAt: typeof snapshot.startsAt === 'string' ? snapshot.startsAt : null,
38443
+ endsAt: typeof snapshot.endsAt === 'string' ? snapshot.endsAt : null,
38444
+ maxRunCount: normalizeAgentPlannedMessagePositiveCount(snapshot.maxRunCount),
38445
+ runCount: normalizeAgentPlannedMessagePositiveCount(snapshot.runCount) || 0,
37981
38446
  };
37982
38447
  }
38448
+ /**
38449
+ * Normalizes one optional positive whole number of a planned-message snapshot.
38450
+ *
38451
+ * @param value - Untrusted interval, run count, or run-count limit.
38452
+ * @returns Whole positive number, or `null` when the value cannot be used.
38453
+ *
38454
+ * @private internal utility of `parseAgentPlannedMessagesSidecar`
38455
+ */
38456
+ function normalizeAgentPlannedMessagePositiveCount(value) {
38457
+ const numericValue = Number(value);
38458
+ return Number.isFinite(numericValue) && numericValue > 0 ? Math.floor(numericValue) : null;
38459
+ }
37983
38460
  /**
37984
38461
  * Checks whether one untrusted sidecar entry describes an already planned message.
37985
38462
  *
@@ -38205,6 +38682,286 @@ function parseAgentMessageRuntimeLogEvent(line) {
38205
38682
  }
38206
38683
  }
38207
38684
 
38685
+ /**
38686
+ * Maximum count of external sources reported for one answered message.
38687
+ *
38688
+ * A single answer can reach the same kind of source many times, so the chip row below it stays
38689
+ * readable by keeping only the sources touched first.
38690
+ *
38691
+ * @private internal constant of agent-message touched external sources
38692
+ */
38693
+ const MAX_TOUCHED_EXTERNAL_SOURCES = 12;
38694
+ /**
38695
+ * Maximum length of one search query shown as the name of a touched search source.
38696
+ *
38697
+ * @private internal constant of agent-message touched external sources
38698
+ */
38699
+ const MAX_SEARCH_QUERY_LENGTH = 48;
38700
+ /**
38701
+ * Separator between the segments of one integration tool name, as in `mcp__gmail__create_draft`.
38702
+ *
38703
+ * @private internal constant of agent-message touched external sources
38704
+ */
38705
+ const INTEGRATION_TOOL_NAME_SEPARATOR = '__';
38706
+ /**
38707
+ * First segment marking one tool name as belonging to an integration.
38708
+ *
38709
+ * @private internal constant of agent-message touched external sources
38710
+ */
38711
+ const INTEGRATION_TOOL_NAME_PREFIX = 'mcp';
38712
+ /**
38713
+ * Names of the harness tools which look something up on the web, indexed for case-insensitive matching.
38714
+ *
38715
+ * @private internal constant of agent-message touched external sources
38716
+ */
38717
+ const WEB_SEARCH_TOOL_NAMES = new Set(['WebSearch', 'web_search'].map((webSearchToolName) => webSearchToolName.toLowerCase()));
38718
+ /**
38719
+ * Matches one shell command invoking a network client, whose arguments therefore address the outside world.
38720
+ *
38721
+ * @private internal constant of agent-message touched external sources
38722
+ */
38723
+ const NETWORK_CLIENT_COMMAND_REGEX = /(^|[\s|;&(])(curl|wget|invoke-webrequest|iwr)\b/iu;
38724
+ /**
38725
+ * Matches one web address inside a shell command.
38726
+ *
38727
+ * @private internal constant of agent-message touched external sources
38728
+ */
38729
+ const COMMAND_URL_REGEX = /https?:\/\/[^\s"'`<>)\]]+/giu;
38730
+ /**
38731
+ * Separators used between the words of one integration name.
38732
+ *
38733
+ * @private internal constant of agent-message touched external sources
38734
+ */
38735
+ const INTEGRATION_NAME_WORD_SEPARATOR_REGEX = /[-_.\s]+/gu;
38736
+ /**
38737
+ * Hostnames which address the machine the agent runs on and are therefore not external.
38738
+ *
38739
+ * @private internal constant of agent-message touched external sources
38740
+ */
38741
+ const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '0.0.0.0', '::1', '[::1]']);
38742
+ /**
38743
+ * Resolves which sources outside the agent one coding harness touched while answering a single message.
38744
+ *
38745
+ * The runtime log streams what the harness really did, so a source counts as touched when the
38746
+ * harness reached it in a tool invocation — calling an integration, fetching a web address,
38747
+ * searching the web, or running a network client in the shell. Only tool payloads are inspected:
38748
+ * tool results and assistant narration are ignored so a service merely mentioned in the
38749
+ * conversation is never reported, and addresses of the machine the agent runs on are dropped
38750
+ * because they are internal.
38751
+ *
38752
+ * @param options - Raw runtime log content of one answered message.
38753
+ * @returns Touched external sources without duplicates, ordered by first appearance.
38754
+ * @private internal utility of the agent-message runtime
38755
+ */
38756
+ function resolveAgentMessageTouchedExternalSources(options) {
38757
+ const touchedExternalSources = new Map();
38758
+ for (const event of parseAgentMessageRuntimeLogEvents(options.logText)) {
38759
+ for (const externalSource of resolveRuntimeLogEventExternalSources(event)) {
38760
+ const externalSourceKey = createExternalSourceKey(externalSource);
38761
+ if (!touchedExternalSources.has(externalSourceKey)) {
38762
+ touchedExternalSources.set(externalSourceKey, externalSource);
38763
+ }
38764
+ if (touchedExternalSources.size === MAX_TOUCHED_EXTERNAL_SOURCES) {
38765
+ return Array.from(touchedExternalSources.values());
38766
+ }
38767
+ }
38768
+ }
38769
+ return Array.from(touchedExternalSources.values());
38770
+ }
38771
+ /**
38772
+ * Collects every external source described by one runtime log event.
38773
+ *
38774
+ * Claude Code reports tool invocations as `tool_use` content blocks, while Codex reports executed
38775
+ * commands, web searches, and integration calls as items.
38776
+ *
38777
+ * @param event - One structured runtime log event.
38778
+ * @returns External sources the event reached, possibly with duplicates.
38779
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38780
+ */
38781
+ function resolveRuntimeLogEventExternalSources(event) {
38782
+ var _a;
38783
+ const externalSources = [];
38784
+ for (const contentBlock of ((_a = event.message) === null || _a === void 0 ? void 0 : _a.content) || []) {
38785
+ if (contentBlock.type === 'tool_use') {
38786
+ externalSources.push(...resolveToolUseExternalSources(contentBlock));
38787
+ }
38788
+ }
38789
+ externalSources.push(...resolveCodexItemExternalSources(event.item));
38790
+ return externalSources;
38791
+ }
38792
+ /**
38793
+ * Collects the external sources reached by one Claude Code tool invocation.
38794
+ *
38795
+ * @param contentBlock - One `tool_use` content block.
38796
+ * @returns External sources the invocation reached.
38797
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38798
+ */
38799
+ function resolveToolUseExternalSources(contentBlock) {
38800
+ const toolName = contentBlock.name || '';
38801
+ const toolInput = contentBlock.input || {};
38802
+ return collectDefinedExternalSources([
38803
+ createIntegrationExternalSource(resolveIntegrationName(toolName)),
38804
+ typeof toolInput.url === 'string' ? createWebsiteExternalSource(toolInput.url) : null,
38805
+ WEB_SEARCH_TOOL_NAMES.has(toolName.toLowerCase()) && typeof toolInput.query === 'string'
38806
+ ? createSearchExternalSource(toolInput.query)
38807
+ : null,
38808
+ ...(typeof toolInput.command === 'string' ? resolveNetworkCommandExternalSources(toolInput.command) : []),
38809
+ ]);
38810
+ }
38811
+ /**
38812
+ * Collects the external sources reached by one Codex runtime item.
38813
+ *
38814
+ * @param item - One Codex item of a runtime log event.
38815
+ * @returns External sources the item reached.
38816
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38817
+ */
38818
+ function resolveCodexItemExternalSources(item) {
38819
+ if (!item) {
38820
+ return [];
38821
+ }
38822
+ return collectDefinedExternalSources([
38823
+ item.type === 'mcp_tool_call'
38824
+ ? createIntegrationExternalSource(item.server || resolveIntegrationName(item.tool || ''))
38825
+ : null,
38826
+ item.type === 'web_search' && typeof item.query === 'string' ? createSearchExternalSource(item.query) : null,
38827
+ ...(typeof item.command === 'string' ? resolveNetworkCommandExternalSources(item.command) : []),
38828
+ ]);
38829
+ }
38830
+ /**
38831
+ * Drops the candidates which turned out not to describe an external source.
38832
+ *
38833
+ * @param candidates - Resolved sources mixed with the candidates that resolved to nothing.
38834
+ * @returns Only the sources which were resolved.
38835
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38836
+ */
38837
+ function collectDefinedExternalSources(candidates) {
38838
+ return candidates.filter((candidate) => candidate !== null);
38839
+ }
38840
+ /**
38841
+ * Resolves the integration a tool name belongs to, following the `mcp__<integration>__<tool>` convention.
38842
+ *
38843
+ * @param toolName - Raw harness tool name.
38844
+ * @returns Raw integration name, or `null` when the tool belongs to no integration.
38845
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38846
+ */
38847
+ function resolveIntegrationName(toolName) {
38848
+ const toolNameSegments = toolName.split(INTEGRATION_TOOL_NAME_SEPARATOR);
38849
+ if (toolNameSegments.length < 3 || toolNameSegments[0].toLowerCase() !== INTEGRATION_TOOL_NAME_PREFIX) {
38850
+ return null;
38851
+ }
38852
+ return toolNameSegments[1] || null;
38853
+ }
38854
+ /**
38855
+ * Creates one touched integration source.
38856
+ *
38857
+ * @param integrationName - Raw integration name, or `null` when there is none.
38858
+ * @returns Touched source, or `null` when the integration cannot be named.
38859
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38860
+ */
38861
+ function createIntegrationExternalSource(integrationName) {
38862
+ const humanizedIntegrationName = humanizeIntegrationName(integrationName || '');
38863
+ if (humanizedIntegrationName === '') {
38864
+ return null;
38865
+ }
38866
+ return { kind: 'integration', name: humanizedIntegrationName };
38867
+ }
38868
+ /**
38869
+ * Creates one touched website source.
38870
+ *
38871
+ * @param rawUrl - Raw web address the harness reached.
38872
+ * @returns Touched source, or `null` when the address is unusable or addresses the local machine.
38873
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38874
+ */
38875
+ function createWebsiteExternalSource(rawUrl) {
38876
+ let url;
38877
+ try {
38878
+ url = new URL(rawUrl.trim());
38879
+ }
38880
+ catch (_a) {
38881
+ return null;
38882
+ }
38883
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
38884
+ return null;
38885
+ }
38886
+ const hostname = url.hostname.toLowerCase();
38887
+ if (hostname === '' || LOCAL_HOSTNAMES.has(hostname) || hostname.endsWith('.localhost')) {
38888
+ return null;
38889
+ }
38890
+ return { kind: 'website', name: hostname, url: url.href };
38891
+ }
38892
+ /**
38893
+ * Creates one touched web search source.
38894
+ *
38895
+ * @param query - Raw query the harness looked up.
38896
+ * @returns Touched source, or `null` when the query is empty.
38897
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38898
+ */
38899
+ function createSearchExternalSource(query) {
38900
+ const normalizedQuery = query.replace(/\s+/gu, ' ').trim();
38901
+ if (normalizedQuery === '') {
38902
+ return null;
38903
+ }
38904
+ return {
38905
+ kind: 'search',
38906
+ name: normalizedQuery.length <= MAX_SEARCH_QUERY_LENGTH
38907
+ ? normalizedQuery
38908
+ : `${normalizedQuery.slice(0, MAX_SEARCH_QUERY_LENGTH).trimEnd()}…`,
38909
+ };
38910
+ }
38911
+ /**
38912
+ * Collects the websites addressed by one shell command running a network client.
38913
+ *
38914
+ * Commands which do not invoke a network client are ignored, so a web address that merely appears
38915
+ * in the text the agent writes into a file is never reported as touched.
38916
+ *
38917
+ * @param command - One executed shell command.
38918
+ * @returns Touched website sources of the command.
38919
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38920
+ */
38921
+ function resolveNetworkCommandExternalSources(command) {
38922
+ if (!NETWORK_CLIENT_COMMAND_REGEX.test(command)) {
38923
+ return [];
38924
+ }
38925
+ const commandUrlRegex = new RegExp(COMMAND_URL_REGEX.source, COMMAND_URL_REGEX.flags);
38926
+ const externalSources = [];
38927
+ let commandUrlMatch = commandUrlRegex.exec(command);
38928
+ while (commandUrlMatch !== null) {
38929
+ const externalSource = createWebsiteExternalSource(commandUrlMatch[0]);
38930
+ if (externalSource) {
38931
+ externalSources.push(externalSource);
38932
+ }
38933
+ commandUrlMatch = commandUrlRegex.exec(command);
38934
+ }
38935
+ return externalSources;
38936
+ }
38937
+ /**
38938
+ * Converts one raw integration name into its user-facing form.
38939
+ *
38940
+ * For example `google-calendar` becomes `Google Calendar`.
38941
+ *
38942
+ * @param integrationName - Raw integration name.
38943
+ * @returns Human-readable integration name, or an empty string when there is nothing to show.
38944
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38945
+ */
38946
+ function humanizeIntegrationName(integrationName) {
38947
+ return integrationName
38948
+ .trim()
38949
+ .split(INTEGRATION_NAME_WORD_SEPARATOR_REGEX)
38950
+ .filter((integrationNameWord) => integrationNameWord !== '')
38951
+ .map((integrationNameWord) => capitalize(integrationNameWord))
38952
+ .join(' ');
38953
+ }
38954
+ /**
38955
+ * Builds the key identifying one external source across the whole run.
38956
+ *
38957
+ * @param externalSource - One touched external source.
38958
+ * @returns Key under which repeated touches of the same source collapse into one.
38959
+ * @private internal helper of `resolveAgentMessageTouchedExternalSources`
38960
+ */
38961
+ function createExternalSourceKey(externalSource) {
38962
+ return `${externalSource.kind}:${externalSource.name.toLowerCase()}`;
38963
+ }
38964
+
38208
38965
  /**
38209
38966
  * Matches one project directory name following the agent `projects/` folder in a file path or command.
38210
38967
  *
@@ -38329,43 +39086,25 @@ function resolveCandidateProjectNames(toolInvocationText) {
38329
39086
  }
38330
39087
 
38331
39088
  /**
38332
- * Resolves which agent projects one answered message viewed or edited.
39089
+ * Resolves which agent projects and which external sources one answered message touched.
38333
39090
  *
38334
39091
  * The runtime log is deleted as soon as the harness run finishes, so this must be called while
38335
- * the log still exists — the Agents Server later shows the reported projects as chips below the
38336
- * answer. Reporting touched projects is best-effort telemetry: an unreadable log or projects
38337
- * folder simply yields no projects instead of failing the already answered message.
39092
+ * the log still exists — the Agents Server later shows everything reported here as chips below
39093
+ * the answer. Reporting touches is best-effort telemetry: an unreadable log or projects folder
39094
+ * simply yields nothing instead of failing the already answered message.
38338
39095
  *
38339
39096
  * @param options - Agent folder path and the live runtime log path of the answered message.
38340
- * @returns Touched project directory names, ordered by first appearance in the run.
39097
+ * @returns Touched projects and external sources, ordered by first appearance in the run.
38341
39098
  */
38342
- async function resolveTouchedAgentProjects(options) {
39099
+ async function resolveAnsweredMessageTouches(options) {
38343
39100
  const [logText, knownProjectNames] = await Promise.all([
38344
39101
  readOptionalTextFile(options.runtimeLogPath),
38345
39102
  listAgentProjectDirectoryNames(options.projectPath),
38346
39103
  ]);
38347
- return resolveAgentMessageTouchedProjectNames({ logText, knownProjectNames });
38348
- }
38349
- /**
38350
- * Lists the direct project directories of one agent folder.
38351
- *
38352
- * @param projectPath - Absolute path of the local agent folder.
38353
- * @returns Project directory names, or an empty list when the agent has no projects folder.
38354
- *
38355
- * @private helper of `resolveTouchedAgentProjects`
38356
- */
38357
- async function listAgentProjectDirectoryNames(projectPath) {
38358
- try {
38359
- const projectsRootEntries = await readdir(join(projectPath, AGENT_PROJECTS_DIRECTORY_PATH), {
38360
- withFileTypes: true,
38361
- });
38362
- return projectsRootEntries
38363
- .filter((projectsRootEntry) => projectsRootEntry.isDirectory())
38364
- .map((projectDirectoryEntry) => projectDirectoryEntry.name);
38365
- }
38366
- catch (_a) {
38367
- return [];
38368
- }
39104
+ return {
39105
+ touchedProjectNames: resolveAgentMessageTouchedProjectNames({ logText, knownProjectNames }),
39106
+ touchedExternalSources: resolveAgentMessageTouchedExternalSources({ logText }),
39107
+ };
38369
39108
  }
38370
39109
  /**
38371
39110
  * Reads one text file and treats a missing runtime log as "nothing was recorded".
@@ -38373,7 +39112,7 @@ async function listAgentProjectDirectoryNames(projectPath) {
38373
39112
  * @param filePath - Absolute path of the file to read.
38374
39113
  * @returns File content, or `null` when it cannot be read.
38375
39114
  *
38376
- * @private helper of `resolveTouchedAgentProjects`
39115
+ * @private helper of `resolveAnsweredMessageTouches`
38377
39116
  */
38378
39117
  async function readOptionalTextFile(filePath) {
38379
39118
  try {
@@ -38690,7 +39429,7 @@ async function runQueuedAgentMessage(options) {
38690
39429
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
38691
39430
  const executionStartedAt = new Date().toISOString();
38692
39431
  let promptRunResult;
38693
- let touchedProjectNames = [];
39432
+ let answeredMessageTouches = { touchedProjectNames: [], touchedExternalSources: [] };
38694
39433
  try {
38695
39434
  try {
38696
39435
  promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) => {
@@ -38706,9 +39445,12 @@ async function runQueuedAgentMessage(options) {
38706
39445
  uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(attemptCount);
38707
39446
  },
38708
39447
  });
38709
- // Note: The runtime log is deleted right after this handler, so the projects this
39448
+ // Note: The runtime log is deleted right after this handler, so everything this
38710
39449
  // answer worked with must be resolved while the log still exists.
38711
- touchedProjectNames = await resolveTouchedAgentProjects({ projectPath, runtimeLogPath: logPath });
39450
+ answeredMessageTouches = await resolveAnsweredMessageTouches({
39451
+ projectPath,
39452
+ runtimeLogPath: logPath,
39453
+ });
38712
39454
  return runResult;
38713
39455
  }, { preserveArtifactsOnSuccess: false });
38714
39456
  }
@@ -38726,6 +39468,12 @@ async function runQueuedAgentMessage(options) {
38726
39468
  }
38727
39469
  const executionFinishedAt = new Date().toISOString();
38728
39470
  await normalizeLineEndingsForAgentRound(projectPath, runOptions, roundChangedFilesSnapshot);
39471
+ // Note: Every agent project keeps its own git history, so what this message did to a project is
39472
+ // committed there before the answer becomes visible and travels with the run report.
39473
+ const projectChanges = await commitAnsweredMessageProjectChanges({
39474
+ agentFolderPath: projectPath,
39475
+ commitMessage: buildAgentMessageCommitMessage(queuedMessage),
39476
+ });
38729
39477
  const finishedTeamWorkspace = await finalizeAgentTeamConversationWorkspace({
38730
39478
  projectPath,
38731
39479
  queuedMessage,
@@ -38747,7 +39495,13 @@ async function runQueuedAgentMessage(options) {
38747
39495
  startedAt: executionStartedAt,
38748
39496
  finishedAt: executionFinishedAt,
38749
39497
  },
38750
- ...(touchedProjectNames.length === 0 ? {} : { touchedProjectNames }),
39498
+ ...(answeredMessageTouches.touchedProjectNames.length === 0
39499
+ ? {}
39500
+ : { touchedProjectNames: answeredMessageTouches.touchedProjectNames }),
39501
+ ...(answeredMessageTouches.touchedExternalSources.length === 0
39502
+ ? {}
39503
+ : { touchedExternalSources: answeredMessageTouches.touchedExternalSources }),
39504
+ ...(projectChanges.length === 0 ? {} : { projectChanges }),
38751
39505
  },
38752
39506
  });
38753
39507
  await commitAnsweredMessageIfEnabled({
@@ -41763,7 +42517,9 @@ function comparePrereleaseIdentifier(firstIdentifier, secondIdentifier) {
41763
42517
  async function $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled = true) {
41764
42518
  const [installedVersion, latestVersion] = await Promise.all([
41765
42519
  $resolveInstalledHarnessVersion(definition),
41766
- isHarnessUpdateCheckEnabled ? $resolveLatestNpmPackageVersion(definition.npmPackageName) : Promise.resolve(null),
42520
+ isHarnessUpdateCheckEnabled
42521
+ ? $resolveLatestNpmPackageVersion(definition.npmPackageName)
42522
+ : Promise.resolve(null),
41767
42523
  ]);
41768
42524
  return {
41769
42525
  definition,
@@ -42447,6 +43203,52 @@ function listDefaultCoderProjectPromptTemplateDisplayPaths() {
42447
43203
  // Note: [🟡] Code for CLI command [init](src/cli/cli-commands/coder/init.ts) should never be published outside of `@promptbook/cli`
42448
43204
  // Note: [💞] Ignore a discrepancy between file name and entity name
42449
43205
 
43206
+ /**
43207
+ * Default wait duration applied before retrying a prompt round after an error (10 minutes).
43208
+ *
43209
+ * @private internal constant of `ptbk coder` wait handling
43210
+ */
43211
+ const DEFAULT_WAIT_AFTER_ERROR_MS$1 = 10 * 60 * 1000;
43212
+ /**
43213
+ * Parses an optional Commander duration string and returns the resolved milliseconds.
43214
+ *
43215
+ * Returns `defaultMs` when the flag was not provided or was provided without a non-empty value.
43216
+ *
43217
+ * @private internal utility of `ptbk coder` wait handling
43218
+ */
43219
+ function parseOptionalWaitDuration(value, defaultMs) {
43220
+ if (typeof value !== 'string' || value === '') {
43221
+ return defaultMs;
43222
+ }
43223
+ return parseDuration(value);
43224
+ }
43225
+ /**
43226
+ * Parses an optional Commander period duration string and returns the resolved milliseconds.
43227
+ *
43228
+ * Returns `undefined` when the flag was not provided or was provided without a non-empty value,
43229
+ * which means the command runs only once instead of repeating itself.
43230
+ *
43231
+ * @throws {NotAllowed} When the duration is not a positive one, because a non-positive period
43232
+ * would repeat the command without ever pausing between two rounds
43233
+ *
43234
+ * @private internal utility of `ptbk coder` wait handling
43235
+ */
43236
+ function parseOptionalPeriodDuration(optionName, value) {
43237
+ if (typeof value !== 'string' || value === '') {
43238
+ return undefined;
43239
+ }
43240
+ const periodMs = parseDuration(value);
43241
+ if (periodMs <= 0) {
43242
+ throw new NotAllowed(spaceTrim$1(`
43243
+ Invalid value for \`${optionName}\`: \`${value}\`.
43244
+
43245
+ Use a **positive** duration like \`5h\`, \`30m\` or \`1h30m\`.
43246
+ `));
43247
+ }
43248
+ return periodMs;
43249
+ }
43250
+ // Note: [💞] Ignore a discrepancy between file name and entity name
43251
+
42450
43252
  /**
42451
43253
  * Initializes `coder ping` command for Promptbook CLI utilities
42452
43254
  *
@@ -42465,6 +43267,7 @@ function $initializeCoderPingCommand(program) {
42465
43267
  - Verifies that the selected harness, model, thinking level and authentication really work
42466
43268
  - Reports the answer of the harness, the response time and the reported usage
42467
43269
  - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
43270
+ - Optional --period keeps the quota window refreshing by pinging once per period until stopped
42468
43271
  - Leaves the project exactly as it was — nothing is read, written, changed or committed
42469
43272
  - Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
42470
43273
  - Use --no-ui to stream the raw harness output instead of only the compact result
@@ -42472,21 +43275,34 @@ function $initializeCoderPingCommand(program) {
42472
43275
  addPromptRunnerSelectionOptions(command);
42473
43276
  addHarnessUpdateOption(command);
42474
43277
  addPromptRunnerRuntimeOptions(command);
43278
+ command.option('--period <duration>', spaceTrim$1(`
43279
+ Keep pinging once per period instead of pinging only once.
43280
+ Accepts durations like 5h, 30m, 1h30m and repeats until it is stopped with CTRL+C.
43281
+ `));
42475
43282
  command.action(handleActionErrors(async (cliOptions) => {
43283
+ const { period: periodValue } = cliOptions;
42476
43284
  const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
42477
43285
  const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
43286
+ // Note: The period is validated before the harness installation check, so a mistyped duration fails fast
43287
+ const periodMs = parseOptionalPeriodDuration('--period', periodValue);
42478
43288
  await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
42479
- // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
42480
- const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
42481
- const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
42482
- const result = await pingCoderHarness({
43289
+ const pingOptions = {
42483
43290
  agentName: runnerOptions.agentName,
42484
43291
  model: runnerOptions.model,
42485
43292
  thinkingLevel: runnerOptions.thinkingLevel,
42486
43293
  allowCredits: runnerOptions.allowCredits,
42487
43294
  shouldPrintLiveOutput: runnerOptions.noUi,
42488
- });
42489
- printCoderPingResult(result);
43295
+ };
43296
+ // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
43297
+ if (periodMs !== undefined) {
43298
+ const { pingCoderHarnessPeriodically } = await Promise.resolve().then(function () { return pingCoderHarnessPeriodically$1; });
43299
+ // Note: This never returns - it keeps pinging until the user stops the process
43300
+ await pingCoderHarnessPeriodically({ ...pingOptions, periodMs });
43301
+ return;
43302
+ }
43303
+ const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
43304
+ const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
43305
+ printCoderPingResult(await pingCoderHarness(pingOptions));
42490
43306
  }));
42491
43307
  }
42492
43308
  // Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
@@ -42923,27 +43739,6 @@ function isTestBeforeMode(value) {
42923
43739
  return TEST_BEFORE_MODE_VALUES.includes(value);
42924
43740
  }
42925
43741
 
42926
- /**
42927
- * Default wait duration applied before retrying a prompt round after an error (10 minutes).
42928
- *
42929
- * @private internal constant of `ptbk coder` wait handling
42930
- */
42931
- const DEFAULT_WAIT_AFTER_ERROR_MS$1 = 10 * 60 * 1000;
42932
- /**
42933
- * Parses an optional Commander duration string and returns the resolved milliseconds.
42934
- *
42935
- * Returns `defaultMs` when the flag was not provided or was provided without a non-empty value.
42936
- *
42937
- * @private internal utility of `ptbk coder` wait handling
42938
- */
42939
- function parseOptionalWaitDuration(value, defaultMs) {
42940
- if (typeof value !== 'string' || value === '') {
42941
- return defaultMs;
42942
- }
42943
- return parseDuration(value);
42944
- }
42945
- // Note: [💞] Ignore a discrepancy between file name and entity name
42946
-
42947
43742
  /**
42948
43743
  * Initializes `coder run` command for Promptbook CLI utilities
42949
43744
  *
@@ -66985,7 +67780,7 @@ function validateBook(source) {
66985
67780
  padBook(validateBook(spaceTrim$1(`
66986
67781
  AI Avatar
66987
67782
 
66988
- PERSONA A friendly AI assistant that helps you with your tasks
67783
+ GOAL Help users with their tasks as a friendly AI assistant.
66989
67784
  `)));
66990
67785
  // <- Note: Not using book`...` notation to avoid strange error in jest unit tests `TypeError: (0 , book_notation_1.book) is not a function`
66991
67786
  // <- TODO: [🐱‍🚀] `GENESIS_BOOK` / `ADAM_BOOK` in `/agents/adam.book`
@@ -72682,6 +73477,23 @@ var findUnwrittenPrompts$1 = /*#__PURE__*/Object.freeze({
72682
73477
  findUnwrittenPrompts: findUnwrittenPrompts
72683
73478
  });
72684
73479
 
73480
+ /**
73481
+ * Formats one unknown error-like value into its readable message without the stack trace.
73482
+ *
73483
+ * Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
73484
+ * inside a branded error, where the stack of the wrapped error would only bury the actual cause.
73485
+ */
73486
+ function formatUnknownErrorMessage(error) {
73487
+ if (error instanceof Error) {
73488
+ return error.message;
73489
+ }
73490
+ if (typeof error === 'string') {
73491
+ return error;
73492
+ }
73493
+ const serializedError = JSON.stringify(error, null, 2);
73494
+ return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
73495
+ }
73496
+
72685
73497
  /**
72686
73498
  * Builds a normalized temporary shell script path for prompt runners.
72687
73499
  */
@@ -72919,6 +73731,60 @@ var printCoderPingResult$1 = /*#__PURE__*/Object.freeze({
72919
73731
  printCoderPingResult: printCoderPingResult
72920
73732
  });
72921
73733
 
73734
+ /**
73735
+ * How often the countdown to the next periodic ping is reported to the console (30 minutes).
73736
+ *
73737
+ * A period like `5h` is meant to be left running unattended, so the countdown is deliberately
73738
+ * coarse — it is a sign of life, not a progress bar.
73739
+ */
73740
+ const CODER_PING_COUNTDOWN_UPDATE_INTERVAL_MS = 30 * 60 * 1000;
73741
+ /**
73742
+ * Pings the selected harness and model once per period until the process is stopped.
73743
+ *
73744
+ * This keeps the quota window of the harness refreshing without any real work, so the window is
73745
+ * always open by the time you need it. The loop never ends on its own — it is stopped with `CTRL+C`
73746
+ * or by killing the process — therefore a failing ping is reported and the next period is started
73747
+ * instead of tearing the whole loop down.
73748
+ */
73749
+ async function pingCoderHarnessPeriodically(options) {
73750
+ const { periodMs, ...pingOptions } = options;
73751
+ console.info(colors.gray(`🏓 Pinging every ${formatDurationMs(periodMs)} until stopped with CTRL+C`));
73752
+ // Note: The loop is intentionally endless - only `CTRL+C` or killing the process ends it
73753
+ for (;;) {
73754
+ const nextPingTimeMs = Date.now() + periodMs;
73755
+ await reportOneCoderPing(pingOptions);
73756
+ await waitUntilNextCoderPing(nextPingTimeMs);
73757
+ }
73758
+ }
73759
+ /**
73760
+ * Sends and reports one ping of the endless loop, keeping the loop alive when the harness fails.
73761
+ */
73762
+ async function reportOneCoderPing(options) {
73763
+ try {
73764
+ printCoderPingResult(await pingCoderHarness(options));
73765
+ }
73766
+ catch (error) {
73767
+ console.error(colors.red(`🏓 Ping failed: ${formatUnknownErrorMessage(error)}`));
73768
+ }
73769
+ }
73770
+ /**
73771
+ * Waits until the wall-clock time of the next ping, reporting how much of the period is left.
73772
+ */
73773
+ async function waitUntilNextCoderPing(nextPingTimeMs) {
73774
+ await waitUntilWorldTimeDeadline({
73775
+ deadlineTimeMs: nextPingTimeMs,
73776
+ pollIntervalMs: CODER_PING_COUNTDOWN_UPDATE_INTERVAL_MS,
73777
+ onTick(remainingDurationMs) {
73778
+ console.info(colors.gray(` Next ping in ${formatDurationMs(remainingDurationMs)}`));
73779
+ },
73780
+ });
73781
+ }
73782
+
73783
+ var pingCoderHarnessPeriodically$1 = /*#__PURE__*/Object.freeze({
73784
+ __proto__: null,
73785
+ pingCoderHarnessPeriodically: pingCoderHarnessPeriodically
73786
+ });
73787
+
72922
73788
  /**
72923
73789
  * Default wait duration applied before retrying a failed prompt round.
72924
73790
  */
@@ -75752,23 +76618,6 @@ function buildCoderIsolationWorktreeDisplayPath(taskName) {
75752
76618
  return getPromptbookTemporaryPath(CODER_ISOLATION_WORKTREES_DIRECTORY_NAME, taskName);
75753
76619
  }
75754
76620
 
75755
- /**
75756
- * Formats one unknown error-like value into its readable message without the stack trace.
75757
- *
75758
- * Use this instead of `formatUnknownErrorDetails` whenever the text is shown to the user, for example
75759
- * inside a branded error, where the stack of the wrapped error would only bury the actual cause.
75760
- */
75761
- function formatUnknownErrorMessage(error) {
75762
- if (error instanceof Error) {
75763
- return error.message;
75764
- }
75765
- if (typeof error === 'string') {
75766
- return error;
75767
- }
75768
- const serializedError = JSON.stringify(error, null, 2);
75769
- return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
75770
- }
75771
-
75772
76621
  /**
75773
76622
  * Git configuration key which lets Git read, write and delete files whose absolute path is longer than
75774
76623
  * the Windows `MAX_PATH` limit of 260 characters.