@promptbook/cli 0.112.0-103 → 0.112.0-105

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 (190) hide show
  1. package/apps/agents-server/src/app/AddAgentButton.tsx +0 -5
  2. package/apps/agents-server/src/app/actions.ts +50 -0
  3. package/apps/agents-server/src/app/admin/image-generator-test/ImageAttachmentsEditor.tsx +19 -3
  4. package/apps/agents-server/src/app/admin/limits/LimitsClient.tsx +11 -12
  5. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +34 -2
  6. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +6 -1
  7. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +13 -1
  8. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +11 -2
  9. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +14 -5
  10. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +7 -1
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +11 -1
  12. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +6 -2
  13. package/apps/agents-server/src/app/api/health/route.ts +18 -0
  14. package/apps/agents-server/src/app/api/images/[filename]/route.ts +6 -2
  15. package/apps/agents-server/src/app/api/internal/agent-runner-limits/route.ts +51 -0
  16. package/apps/agents-server/src/app/api/upload/route.ts +48 -12
  17. package/apps/agents-server/src/app/layout.tsx +13 -0
  18. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +1 -4
  19. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadAvailabilityContext.tsx +50 -0
  20. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadUnavailableNotice.tsx +45 -0
  21. package/apps/agents-server/src/components/Header/Header.tsx +0 -11
  22. package/apps/agents-server/src/components/Header/useHeaderAgentMenus.tsx +0 -5
  23. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +85 -76
  24. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +7 -3
  25. package/apps/agents-server/src/components/NewAgentDialog/NewAgentWizardKnowledgeStep.tsx +6 -0
  26. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentDialog.tsx +39 -16
  27. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentWizardKnowledgeState.ts +8 -1
  28. package/apps/agents-server/src/constants/defaultAgentAvatarVisual.ts +1 -1
  29. package/apps/agents-server/src/constants/serverLimits.ts +22 -2
  30. package/apps/agents-server/src/database/migrations/2026-06-0200-default-agent-avatar-visual-octopus3d3.sql +16 -0
  31. package/apps/agents-server/src/database/seedDefaultAgents.ts +218 -0
  32. package/apps/agents-server/src/middleware.ts +2 -1
  33. package/apps/agents-server/src/tools/$provideCdnForServer.ts +114 -9
  34. package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +27 -4
  35. package/apps/agents-server/src/utils/defaultAgents/loadDefaultAgentBooks.ts +103 -0
  36. package/apps/agents-server/src/utils/knowledge/createInlineKnowledgeSourceUploader.ts +24 -5
  37. package/apps/agents-server/src/utils/serverLimits.ts +26 -1
  38. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +1 -85
  39. package/apps/agents-server/src/utils/shareTargetPayloads.ts +20 -2
  40. package/apps/agents-server/src/utils/upload/fileUploadAvailability.ts +91 -0
  41. package/apps/agents-server/src/utils/upload/uploadFileToServer.ts +46 -2
  42. package/esm/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  43. package/esm/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  44. package/esm/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  45. package/esm/index.es.js +1109 -35
  46. package/esm/index.es.js.map +1 -1
  47. package/esm/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  48. package/esm/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  49. package/esm/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  50. package/esm/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  51. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  52. package/esm/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  53. package/esm/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  54. package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  55. package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  56. package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  57. package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  58. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  59. package/esm/src/version.d.ts +1 -1
  60. package/package.json +1 -1
  61. package/src/avatars/types/AvatarVisualDefinition.ts +1 -0
  62. package/src/avatars/visuals/avatarVisualRegistry.ts +2 -0
  63. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +902 -0
  64. package/src/book-components/BookEditor/BookEditor.tsx +10 -7
  65. package/src/book-components/BookEditor/BookEditorMonaco.tsx +3 -1
  66. package/src/book-components/BookEditor/BookEditorTheme.ts +32 -0
  67. package/src/book-components/BookEditor/useBookEditorMonacoLanguage.ts +12 -15
  68. package/src/book-components/BookEditor/useBookEditorMonacoLifecycle.ts +1 -5
  69. package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +2 -1
  70. package/src/cli/cli-commands/agent-folder/agentProjectPaths.ts +7 -0
  71. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +109 -9
  72. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +132 -4
  73. package/src/other/templates/getTemplatesPipelineCollection.ts +690 -747
  74. package/src/utils/agents/resolveAgentAvatarImageUrl.ts +1 -1
  75. package/src/version.ts +2 -2
  76. package/src/versions.txt +2 -1
  77. package/umd/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  78. package/umd/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  79. package/umd/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  80. package/umd/index.umd.js +1109 -35
  81. package/umd/index.umd.js.map +1 -1
  82. package/umd/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  83. package/umd/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  84. package/umd/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  85. package/umd/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  86. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  87. package/umd/src/avatars/visuals/octopus3d3AvatarVisual.d.ts +7 -0
  88. package/umd/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  89. package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  90. package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  91. package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  92. package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  93. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  94. package/umd/src/version.d.ts +1 -1
  95. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +0 -108
  96. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +0 -117
  97. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +0 -119
  98. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +0 -74
  99. package/apps/agents-server/tests/e2e/authentication-and-navigation.spec.ts.todo +0 -178
  100. package/src/_packages/browser.index.ts +0 -31
  101. package/src/_packages/browser.readme.md +0 -43
  102. package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.test.ts.todo +0 -265
  103. package/src/book-components/BookEditor/BookEditorMonaco.test.tsx.todo +0 -115
  104. package/src/book-components/Chat/utils/renderMarkdown.test.ts.tmp +0 -199
  105. package/src/collection/agent-collection/constructors/agent-collection-in-directory/AgentCollectionInDirectory.test.ts.todo +0 -131
  106. package/src/commands/_common/parseCommand.test.ts.todo +0 -48
  107. package/src/commitments/META_LINK/META_LINK.test.ts.todo +0 -75
  108. package/src/conversion/validation/pipelineStringToJson-errors.test.ts.todo +0 -33
  109. package/src/dialogs/simple-prompt/SimplePromptInterfaceTools.ts +0 -51
  110. package/src/executables/browsers/locateSafari.test.ts.tmp +0 -15
  111. package/src/execution/PromptbookFetch.test-type.ts +0 -14
  112. package/src/execution/createPipelineExecutor/00-createPipelineExecutor.test.ts.todo +0 -0
  113. package/src/execution/execution-report/executionReportJsonToString.test.ts.todo +0 -83
  114. package/src/execution/utils/usageToHuman.test.ts.todo +0 -80
  115. package/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.ts +0 -76
  116. package/src/llm-providers/_common/utils/assertUniqueModels.ts +0 -27
  117. package/src/llm-providers/_multiple/playground/playground.ts +0 -141
  118. package/src/llm-providers/_multiple/playground/tsconfig.json +0 -19
  119. package/src/llm-providers/agent/playground/playground.ts +0 -190
  120. package/src/llm-providers/agent/playground/tsconfig.json +0 -19
  121. package/src/llm-providers/anthropic-claude/playground/playground.ts +0 -99
  122. package/src/llm-providers/anthropic-claude/playground/tsconfig.json +0 -19
  123. package/src/llm-providers/azure-openai/playground/playground.ts +0 -101
  124. package/src/llm-providers/azure-openai/playground/tsconfig.json +0 -19
  125. package/src/llm-providers/ollama/playground/playground.ts +0 -120
  126. package/src/llm-providers/ollama/playground/tsconfig.json +0 -19
  127. package/src/llm-providers/openai/playground/playground.ts +0 -406
  128. package/src/llm-providers/openai/playground/tsconfig.json +0 -19
  129. package/src/llm-providers/remote/playground/playground.ts +0 -144
  130. package/src/llm-providers/remote/playground/tsconfig.json +0 -19
  131. package/src/llm-providers/vercel/playground/playground.ts +0 -133
  132. package/src/llm-providers/vercel/playground/tsconfig.json +0 -19
  133. package/src/personas/preparePersona.test.ts.todo +0 -126
  134. package/src/playground/backup/_playground-boilerplate.ts.txt +0 -37
  135. package/src/playground/backup/playground-agent-os.txt +0 -62
  136. package/src/playground/backup/playground-brj-app.ts.txt +0 -302
  137. package/src/playground/backup/playground-browser-playwright.txt +0 -110
  138. package/src/playground/backup/playground-claude-mcp.txt +0 -43
  139. package/src/playground/backup/playground-document-conversion.txt +0 -84
  140. package/src/playground/backup/playground-glob.ts.txt +0 -42
  141. package/src/playground/backup/playground-mcp-server.txt +0 -1
  142. package/src/playground/backup/playground-openai-agent-kit.txt +0 -73
  143. package/src/playground/backup/playground-openai-function-calling.txt +0 -131
  144. package/src/playground/backup/playground-openai-streaming.ts.txt +0 -68
  145. package/src/playground/backup/playground-scrape-knowledge.txt +0 -65
  146. package/src/playground/backup/playground-scraperFetch.ts.txt +0 -44
  147. package/src/playground/backup/playground-using-openai-compatible-route-on-agents-server.ts.txt +0 -49
  148. package/src/playground/backup/playground-write-pavolhejny-bio.txt +0 -120
  149. package/src/playground/permanent/_boilerplate.ts +0 -54
  150. package/src/playground/permanent/agent-with-browser-playground.ts +0 -92
  151. package/src/playground/permanent/error-handling-playground.ts +0 -103
  152. package/src/playground/playground.ts +0 -36
  153. package/src/playground/tsconfig.json +0 -19
  154. package/src/scrapers/_boilerplate/BoilerplateScraper.test.ts.todo +0 -73
  155. package/src/scrapers/_boilerplate/playground/boilerplate-scraper-playground.ts +0 -79
  156. package/src/scrapers/_boilerplate/playground/tsconfig.json +0 -19
  157. package/src/scrapers/_common/utils/files/blobToDataurl.test.ts.todo +0 -17
  158. package/src/scrapers/_common/utils/files/dataurlToBlob.test.ts.todo +0 -52
  159. package/src/scrapers/_common/utils/files/isValidDataurl.test.ts.todo +0 -42
  160. package/src/scrapers/_common/utils/files/shorten.test.ts.todo +0 -13
  161. package/src/scrapers/document/playground/document-scraper-playground.ts +0 -80
  162. package/src/scrapers/document/playground/tsconfig.json +0 -19
  163. package/src/scrapers/document-legacy/playground/legacy-document-scraper-playground.ts +0 -80
  164. package/src/scrapers/document-legacy/playground/tsconfig.json +0 -19
  165. package/src/scrapers/markdown/playground/markdown-scraper-playground.ts +0 -74
  166. package/src/scrapers/markdown/playground/tsconfig.json +0 -19
  167. package/src/scrapers/markitdown/MarkitdownScraper.test.ts.todo +0 -132
  168. package/src/scrapers/markitdown/playground/markitdown-scraper-playground.ts +0 -91
  169. package/src/scrapers/markitdown/playground/tsconfig.json +0 -19
  170. package/src/scrapers/pdf/PdfScraper.test.ts.todo +0 -52
  171. package/src/scrapers/pdf/playground/pdf-scraper-playground.ts +0 -75
  172. package/src/scrapers/pdf/playground/tsconfig.json +0 -19
  173. package/src/scrapers/website/playground/tsconfig.json +0 -19
  174. package/src/scrapers/website/playground/website-scraper-playground.ts +0 -82
  175. package/src/storage/_common/PromptbookStorage.test-type.ts +0 -14
  176. package/src/storage/local-storage/getIndexedDbStorage.ts +0 -36
  177. package/src/storage/local-storage/getLocalStorage.ts +0 -33
  178. package/src/storage/local-storage/getSessionStorage.ts +0 -33
  179. package/src/storage/local-storage/utils/IndexedDbStorageOptions.ts +0 -16
  180. package/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.ts +0 -58
  181. package/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.ts +0 -45
  182. package/src/transpilers/formatted-book-in-markdown/FormattedBookInMarkdownTranspiler.test.ts.todo +0 -35
  183. package/src/transpilers/openai-sdk/playground/playground.ts +0 -85
  184. package/src/transpilers/openai-sdk/playground/tmp/chatbot-openaisdk-1.js +0 -194
  185. package/src/transpilers/openai-sdk/playground/tmp/package.json +0 -3
  186. package/src/transpilers/openai-sdk/playground/tsconfig.json +0 -18
  187. package/src/utils/editable/utils/findUsableParameters.test.ts.todo +0 -43
  188. package/src/utils/editable/utils/stringifyPipelineJson.test.ts.todo +0 -38
  189. package/src/utils/markdown/prettifyMarkdown.test.ts.tmp +0 -42
  190. package/src/utils/serialization/serializeToPromptbookJavascript.test.ts.todo +0 -116
@@ -5,20 +5,52 @@ import { VercelBlobStorage } from '../utils/cdn/classes/VercelBlobStorage';
5
5
  import { IIFilesStorageWithCdn } from '../utils/cdn/interfaces/IFilesStorage';
6
6
 
7
7
  /**
8
- * Cache of CDN instance
8
+ * Options controlling CDN storage construction for the current server request.
9
+ */
10
+ export type ProvideCdnForServerOptions = {
11
+ /**
12
+ * Public URL used when generating file links.
13
+ */
14
+ readonly cdnPublicUrl?: URL;
15
+ };
16
+
17
+ /**
18
+ * Region expected by the bundled VersityGW S3-compatible storage.
19
+ *
20
+ * @private internal default for `$provideCdnForServer`
21
+ */
22
+ const SELF_CONTAINED_S3_DEFAULT_REGION = 'us-east-1';
23
+
24
+ /**
25
+ * Legacy fallback used by Cloudflare R2-style external S3 configuration.
26
+ *
27
+ * @private internal default for `$provideCdnForServer`
28
+ */
29
+ const EXTERNAL_S3_DEFAULT_REGION = 'auto';
30
+
31
+ /**
32
+ * Cache of CDN instances by public URL.
9
33
  *
10
34
  * @private internal cache for `$provideCdnForServer`
11
35
  */
12
- let cdn: IIFilesStorageWithCdn | null = null;
36
+ const cdnByPublicUrl = new Map<string, IIFilesStorageWithCdn>();
13
37
 
14
38
  /**
15
39
  * Provides a CDN storage interface for server-side file operations, with caching to reuse instances.
40
+ *
41
+ * @param options - Optional request-aware CDN public URL override.
42
+ * @returns CDN storage interface.
16
43
  */
17
- export function $provideCdnForServer(): IIFilesStorageWithCdn {
44
+ export function $provideCdnForServer(options: ProvideCdnForServerOptions = {}): IIFilesStorageWithCdn {
45
+ const cdnPublicUrl = options.cdnPublicUrl || resolveConfiguredCdnPublicUrl();
46
+ const cdnCacheKey = cdnPublicUrl.href;
47
+ let cdn = cdnByPublicUrl.get(cdnCacheKey);
48
+
18
49
  if (!cdn) {
19
- const inner = createCdnStorageForServer();
50
+ const inner = createCdnStorageForServer(cdnPublicUrl);
20
51
  const supabase = $provideSupabaseForServer();
21
52
  cdn = new TrackedFilesStorage(inner, supabase);
53
+ cdnByPublicUrl.set(cdnCacheKey, cdn);
22
54
  }
23
55
 
24
56
  return cdn;
@@ -27,9 +59,10 @@ export function $provideCdnForServer(): IIFilesStorageWithCdn {
27
59
  /**
28
60
  * Creates the configured CDN storage implementation for server-side file operations.
29
61
  *
62
+ * @param cdnPublicUrl - Public URL used to build deterministic file links.
30
63
  * @private helper of `$provideCdnForServer`
31
64
  */
32
- function createCdnStorageForServer(): IIFilesStorageWithCdn {
65
+ function createCdnStorageForServer(cdnPublicUrl: URL): IIFilesStorageWithCdn {
33
66
  if (isS3CompatibleStorageSelected()) {
34
67
  return new DigitalOceanSpaces({
35
68
  bucket: process.env.CDN_BUCKET!,
@@ -37,27 +70,63 @@ function createCdnStorageForServer(): IIFilesStorageWithCdn {
37
70
  endpoint: process.env.CDN_ENDPOINT!,
38
71
  accessKeyId: process.env.CDN_ACCESS_KEY_ID!,
39
72
  secretAccessKey: process.env.CDN_SECRET_ACCESS_KEY!,
40
- cdnPublicUrl: new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!),
73
+ cdnPublicUrl,
41
74
  gzip: true,
42
75
  forcePathStyle: process.env.CDN_FORCE_PATH_STYLE === 'true',
43
- region: process.env.CDN_REGION || 'auto',
76
+ region: resolveS3CompatibleStorageRegion(),
44
77
  });
45
78
  }
46
79
 
47
80
  return new VercelBlobStorage({
48
81
  token: process.env.VERCEL_BLOB_READ_WRITE_TOKEN!,
49
82
  pathPrefix: process.env.NEXT_PUBLIC_CDN_PATH_PREFIX || '',
50
- cdnPublicUrl: new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!),
83
+ cdnPublicUrl,
51
84
  });
52
85
  }
53
86
 
87
+ /**
88
+ * Resolves the CDN public URL from environment configuration.
89
+ *
90
+ * @returns Configured CDN public URL.
91
+ * @private helper of `$provideCdnForServer`
92
+ */
93
+ function resolveConfiguredCdnPublicUrl(): URL {
94
+ return new URL(process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!);
95
+ }
96
+
97
+ /**
98
+ * Resolves the public URL that should be used for CDN links for one server request.
99
+ *
100
+ * Self-contained S3 is proxied through the current server domain. The configured
101
+ * CDN public URL still owns the `/s3/<bucket>` path, but the origin must follow
102
+ * the active server so uploads are not published under the VPS raw IP address.
103
+ *
104
+ * @param serverPublicUrl - Public URL of the active server.
105
+ * @returns Request-aware CDN public URL.
106
+ */
107
+ export function resolveCdnPublicUrlForServer(serverPublicUrl: URL): URL {
108
+ const configuredCdnPublicUrl = resolveConfiguredCdnPublicUrl();
109
+
110
+ if (!isSelfContainedS3StorageSelected()) {
111
+ return configuredCdnPublicUrl;
112
+ }
113
+
114
+ const cdnPublicUrl = new URL(configuredCdnPublicUrl.href);
115
+ cdnPublicUrl.protocol = serverPublicUrl.protocol;
116
+ cdnPublicUrl.host = serverPublicUrl.host;
117
+ cdnPublicUrl.username = '';
118
+ cdnPublicUrl.password = '';
119
+
120
+ return cdnPublicUrl;
121
+ }
122
+
54
123
  /**
55
124
  * Checks whether the current environment should use the S3-compatible storage implementation.
56
125
  *
57
126
  * @private helper of `$provideCdnForServer`
58
127
  */
59
128
  function isS3CompatibleStorageSelected(): boolean {
60
- const storageMode = (process.env.PTBK_FILE_STORAGE_MODE || process.env.CDN_PROVIDER || '').toLowerCase();
129
+ const storageMode = getS3CompatibleStorageMode();
61
130
  const isS3StorageMode =
62
131
  storageMode === 's3' || storageMode === 'external-s3' || storageMode === 'self-contained-s3';
63
132
 
@@ -68,6 +137,42 @@ function isS3CompatibleStorageSelected(): boolean {
68
137
  return !process.env.VERCEL_BLOB_READ_WRITE_TOKEN && hasS3CompatibleStorageConfiguration();
69
138
  }
70
139
 
140
+ /**
141
+ * Resolves the configured S3-compatible storage mode.
142
+ *
143
+ * @private helper of `$provideCdnForServer`
144
+ */
145
+ function getS3CompatibleStorageMode(): string {
146
+ return (process.env.PTBK_FILE_STORAGE_MODE || process.env.CDN_PROVIDER || '').toLowerCase();
147
+ }
148
+
149
+ /**
150
+ * Checks whether the bundled self-contained S3 storage mode is selected.
151
+ *
152
+ * @returns `true` when self-contained S3 is selected.
153
+ */
154
+ export function isSelfContainedS3StorageSelected(): boolean {
155
+ return getS3CompatibleStorageMode() === 'self-contained-s3';
156
+ }
157
+
158
+ /**
159
+ * Resolves the S3 signing region used by AWS SDK requests.
160
+ *
161
+ * @private helper of `$provideCdnForServer`
162
+ */
163
+ function resolveS3CompatibleStorageRegion(): string {
164
+ const configuredRegion = process.env.CDN_REGION?.trim();
165
+ if (configuredRegion) {
166
+ return configuredRegion;
167
+ }
168
+
169
+ if (isSelfContainedS3StorageSelected()) {
170
+ return SELF_CONTAINED_S3_DEFAULT_REGION;
171
+ }
172
+
173
+ return EXTERNAL_S3_DEFAULT_REGION;
174
+ }
175
+
71
176
  /**
72
177
  * Checks whether all S3-compatible storage environment variables are present.
73
178
  *
@@ -56,15 +56,21 @@ type PseudoAgentRouteTarget = {
56
56
  export type AgentRouteTarget = LocalAgentRouteTarget | RemoteAgentRouteTarget | PseudoAgentRouteTarget;
57
57
 
58
58
  /**
59
- * Resolves any incoming `/agents/:agentId` token into a canonical target URL.
59
+ * Resolves any incoming `/agents/:agentId` token into a canonical target URL without memoization.
60
60
  *
61
61
  * Supported inputs include plain IDs/names, `@name`, `{name}`, `{id}`, and absolute `/agents/...` URLs.
62
62
  * Pseudo-agent tokens such as `{User}`, `{Void}`, or `{Null}` resolve to dedicated documentation pages.
63
63
  *
64
64
  * @param rawReference - Raw decoded route parameter value.
65
+ * @param options - Optional cache-bypass controls used by create-agent flows.
65
66
  * @returns Canonical local/remote route target or `null` when the reference cannot be resolved.
66
67
  */
67
- const getCachedAgentRouteTarget = cache(async (rawReference: string): Promise<AgentRouteTarget | null> => {
68
+ async function resolveAgentRouteTargetUncached(
69
+ rawReference: string,
70
+ options?: {
71
+ readonly forceRefresh?: boolean;
72
+ },
73
+ ): Promise<AgentRouteTarget | null> {
68
74
  const parsedBookScopedAgentIdentifier = parseBookScopedAgentIdentifier(rawReference);
69
75
  if (parsedBookScopedAgentIdentifier) {
70
76
  const { publicUrl } = await $provideServer();
@@ -96,7 +102,7 @@ const getCachedAgentRouteTarget = cache(async (rawReference: string): Promise<Ag
96
102
  };
97
103
  }
98
104
 
99
- const resolver = await $provideAgentReferenceResolver();
105
+ const resolver = await $provideAgentReferenceResolver({ forceRefresh: options?.forceRefresh });
100
106
  let resolvedUrlValue: string;
101
107
 
102
108
  try {
@@ -137,15 +143,32 @@ const getCachedAgentRouteTarget = cache(async (rawReference: string): Promise<Ag
137
143
  canonicalAgentId,
138
144
  canonicalUrl: `${localServerUrl}${AGENT_PATH_PREFIX}${encodeURIComponent(canonicalAgentId)}`,
139
145
  };
146
+ }
147
+
148
+ /**
149
+ * Memoized route-target resolver used for normal page rendering.
150
+ */
151
+ const getCachedAgentRouteTarget = cache(async (rawReference: string): Promise<AgentRouteTarget | null> => {
152
+ return resolveAgentRouteTargetUncached(rawReference);
140
153
  });
141
154
 
142
155
  /**
143
156
  * Resolves any incoming `/agents/:agentId` token into a canonical target URL.
144
157
  *
145
158
  * @param rawReference - Raw decoded route parameter value.
159
+ * @param options - Optional cache-bypass controls used by create-agent flows.
146
160
  * @returns Canonical local/remote route target or `null` when the reference cannot be resolved.
147
161
  */
148
- export async function resolveAgentRouteTarget(rawReference: string): Promise<AgentRouteTarget | null> {
162
+ export async function resolveAgentRouteTarget(
163
+ rawReference: string,
164
+ options?: {
165
+ readonly forceRefresh?: boolean;
166
+ },
167
+ ): Promise<AgentRouteTarget | null> {
168
+ if (options?.forceRefresh) {
169
+ return resolveAgentRouteTargetUncached(rawReference, options);
170
+ }
171
+
149
172
  return getCachedAgentRouteTarget(rawReference);
150
173
  }
151
174
 
@@ -0,0 +1,103 @@
1
+ import { access, readFile, readdir } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import type { string_book } from '../../../../../src/book-2.0/agent-source/string_book';
4
+ import { DatabaseError } from '../../../../../src/errors/DatabaseError';
5
+ import { spaceTrim } from 'spacetrim';
6
+
7
+ /**
8
+ * Options for loading bundled default agent books.
9
+ *
10
+ * @private utility of Agents Server default-agent seeding
11
+ */
12
+ export type LoadDefaultAgentBooksOptions = {
13
+ /**
14
+ * Optional explicit directory containing `*.book` files.
15
+ */
16
+ readonly defaultAgentDirectory?: string | null;
17
+ };
18
+
19
+ /**
20
+ * Candidate directories where bundled default agent books can be located.
21
+ *
22
+ * The app test suite runs from `apps/agents-server`, local development usually runs from the repository root, and
23
+ * installer scripts can pass an explicit directory through `LoadDefaultAgentBooksOptions`.
24
+ *
25
+ * @private utility of Agents Server default-agent seeding
26
+ */
27
+ const DEFAULT_AGENT_DIRECTORY_CANDIDATES = [
28
+ resolve(process.cwd(), 'agents', 'default'),
29
+ resolve(process.cwd(), '..', '..', 'agents', 'default'),
30
+ resolve(__dirname, '../../../../../agents/default'),
31
+ ] as const;
32
+
33
+ /**
34
+ * Loads all default agent books in deterministic filename order.
35
+ *
36
+ * @param options - Optional explicit default-agent directory.
37
+ * @returns Sorted default agent sources.
38
+ *
39
+ * @private utility of Agents Server default-agent seeding
40
+ */
41
+ export async function loadDefaultAgentBooks(
42
+ options: LoadDefaultAgentBooksOptions = {},
43
+ ): Promise<ReadonlyArray<string_book>> {
44
+ const defaultAgentDirectory = await resolveDefaultAgentDirectory(options);
45
+ const directoryEntries = await readdir(defaultAgentDirectory);
46
+ const defaultAgentFilenames = directoryEntries
47
+ .filter((entry) => entry.toLowerCase().endsWith('.book'))
48
+ .sort((leftFilename, rightFilename) => leftFilename.localeCompare(rightFilename));
49
+
50
+ return Promise.all(
51
+ defaultAgentFilenames.map(async (filename) => {
52
+ const filePath = resolve(defaultAgentDirectory, filename);
53
+ return (await readFile(filePath, 'utf-8')) as string_book;
54
+ }),
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Resolves the repository directory that stores default agent books.
60
+ *
61
+ * @param options - Optional explicit default-agent directory.
62
+ * @returns Absolute path to `agents/default`.
63
+ *
64
+ * @private utility of Agents Server default-agent seeding
65
+ */
66
+ export async function resolveDefaultAgentDirectory(options: LoadDefaultAgentBooksOptions = {}): Promise<string> {
67
+ const directoryCandidates = [
68
+ ...(options.defaultAgentDirectory ? [resolve(options.defaultAgentDirectory)] : []),
69
+ ...DEFAULT_AGENT_DIRECTORY_CANDIDATES,
70
+ ];
71
+
72
+ for (const directoryCandidate of directoryCandidates) {
73
+ try {
74
+ await access(directoryCandidate);
75
+ return directoryCandidate;
76
+ } catch {
77
+ // Continue to the next candidate directory.
78
+ }
79
+ }
80
+
81
+ throw new DatabaseError(
82
+ spaceTrim(
83
+ (block) => `
84
+ Failed to locate the default Agents Server books directory.
85
+
86
+ Checked:
87
+ ${block(directoryCandidates.map((candidate) => `- \`${normalizePathForLogs(candidate)}\``).join('\n'))}
88
+ `,
89
+ ),
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Normalizes path separators for diagnostics.
95
+ *
96
+ * @param value - Raw filesystem path.
97
+ * @returns Slash-normalized path.
98
+ *
99
+ * @private utility of Agents Server default-agent seeding
100
+ */
101
+ function normalizePathForLogs(value: string): string {
102
+ return value.split('\\').join('/');
103
+ }
@@ -1,8 +1,15 @@
1
- import { $provideCdnForServer } from '@/src/tools/$provideCdnForServer';
1
+ import {
2
+ $provideCdnForServer,
3
+ isSelfContainedS3StorageSelected,
4
+ resolveCdnPublicUrlForServer,
5
+ } from '@/src/tools/$provideCdnForServer';
6
+ import { $provideServer } from '@/src/tools/$provideServer';
7
+ import { NotAllowed } from '../../../../../src/errors/NotAllowed';
2
8
  import { InlineKnowledgeSourceUploader } from '../../../../../src/utils/knowledge/inlineKnowledgeSource';
3
9
  import type { string_knowledge_source_link } from '../../../../../src/types/typeAliases';
4
10
  import { getSafeCdnPath } from '../cdn/utils/getSafeCdnPath';
5
11
  import { getUserFileCdnKey } from '../cdn/utils/getUserFileCdnKey';
12
+ import { resolveFileUploadAvailability } from '../upload/fileUploadAvailability';
6
13
 
7
14
  /**
8
15
  * Options for create inline knowledge uploader.
@@ -23,18 +30,30 @@ const uploadCache = new Map<string, Promise<string_knowledge_source_link>>();
23
30
  export function createInlineKnowledgeSourceUploader(
24
31
  options: CreateInlineKnowledgeUploaderOptions = {},
25
32
  ): InlineKnowledgeSourceUploader {
26
- const cdn = $provideCdnForServer();
27
33
  const { purpose = 'KNOWLEDGE', userId } = options;
28
34
 
29
35
  return async (source) => {
36
+ const providedServer = await $provideServer();
37
+ const fileUploadAvailability = resolveFileUploadAvailability({
38
+ serverId: providedServer.id,
39
+ serverPublicUrl: providedServer.publicUrl,
40
+ isSelfContainedS3StorageSelected: isSelfContainedS3StorageSelected(),
41
+ });
42
+ if (!fileUploadAvailability.isUploadAvailable) {
43
+ throw new NotAllowed(fileUploadAvailability.message || 'File uploads are not available for this server.');
44
+ }
45
+
46
+ const cdnPublicUrl = resolveCdnPublicUrlForServer(providedServer.publicUrl);
47
+ const cdn = $provideCdnForServer({ cdnPublicUrl });
30
48
  const rawKey = getUserFileCdnKey(source.buffer, source.filename);
31
49
  const safeKey = getSafeCdnPath({
32
50
  pathname: rawKey,
33
51
  pathPrefix: process.env.NEXT_PUBLIC_CDN_PATH_PREFIX,
34
52
  });
53
+ const uploadCacheKey = `${providedServer.tablePrefix}:${cdnPublicUrl.href}:${safeKey}`;
35
54
 
36
- if (uploadCache.has(safeKey)) {
37
- return uploadCache.get(safeKey)!;
55
+ if (uploadCache.has(uploadCacheKey)) {
56
+ return uploadCache.get(uploadCacheKey)!;
38
57
  }
39
58
 
40
59
  const promise: Promise<string_knowledge_source_link> = (async () => {
@@ -48,7 +67,7 @@ export function createInlineKnowledgeSourceUploader(
48
67
  return cdn.getItemUrl(safeKey).href;
49
68
  })();
50
69
 
51
- uploadCache.set(safeKey, promise);
70
+ uploadCache.set(uploadCacheKey, promise);
52
71
  return promise;
53
72
  };
54
73
  }
@@ -39,6 +39,15 @@ export type SpawnAgentLimits = {
39
39
  readonly rateLimitWindowMs: number;
40
40
  };
41
41
 
42
+ /**
43
+ * Dedicated subset consumed by the local coding-agent runner.
44
+ *
45
+ * @private internal Agents Server type
46
+ */
47
+ export type LocalAgentRunnerLimits = {
48
+ readonly maxFailedAttempts: number;
49
+ };
50
+
42
51
  /**
43
52
  * Row shape loaded from the dedicated `ServerLimit` table.
44
53
  *
@@ -250,6 +259,20 @@ export async function getSpawnAgentLimits(): Promise<SpawnAgentLimits> {
250
259
  };
251
260
  }
252
261
 
262
+ /**
263
+ * Loads the local coding-agent runner retry limits.
264
+ *
265
+ * @returns Dedicated local agent runner limits.
266
+ *
267
+ * @private internal Agents Server helper
268
+ */
269
+ export async function getLocalAgentRunnerLimits(): Promise<LocalAgentRunnerLimits> {
270
+ const limits = await getServerLimits();
271
+ return {
272
+ maxFailedAttempts: limits[SERVER_LIMIT_KEYS.LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS],
273
+ };
274
+ }
275
+
253
276
  /**
254
277
  * Normalizes one arbitrary key/value object into the supported dedicated server-limit payload.
255
278
  *
@@ -277,7 +300,9 @@ export function normalizeServerLimitValues(rawValue: Record<string, unknown>): S
277
300
  */
278
301
  async function loadLegacyServerLimits(): Promise<Partial<Record<ServerLimitKey, unknown>>> {
279
302
  const metadata = await getMetadataMap(DEPRECATED_LIMIT_METADATA_KEYS);
280
- const normalizedToolUsageLimits = normalizeToolUsageLimits(parseJsonValue(metadata[TOOL_USAGE_LIMITS_METADATA_KEY]));
303
+ const normalizedToolUsageLimits = normalizeToolUsageLimits(
304
+ parseJsonValue(metadata[TOOL_USAGE_LIMITS_METADATA_KEY]),
305
+ );
281
306
 
282
307
  return {
283
308
  [SERVER_LIMIT_KEYS.TIMEOUT_MAX_ACTIVE_PER_CHAT]: normalizedToolUsageLimits.timeout.maxActivePerChat,
@@ -1,26 +1,9 @@
1
- import { access, readFile, readdir } from 'node:fs/promises';
2
- import { resolve } from 'node:path';
3
- import type { string_book } from '../../../../../../src/book-2.0/agent-source/string_book';
4
1
  import { createAgentPersistenceRecords } from '../../../../../../src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords';
5
- import { DatabaseError } from '../../../../../../src/errors/DatabaseError';
6
2
  import type { Client } from 'pg';
7
- import { spaceTrim } from 'spacetrim';
3
+ import { loadDefaultAgentBooks } from '../../defaultAgents/loadDefaultAgentBooks';
8
4
  import { createInsertStatement, quoteIdentifier, type SqlRecorder } from './createSqlRecorder';
9
5
  import type { NormalizedCreateServerInput } from './normalizeCreateServerInput';
10
6
 
11
- /**
12
- * Candidate directories where bundled default agent books can be located.
13
- *
14
- * The app test suite runs from `apps/agents-server`, while local development usually runs from the repository root.
15
- *
16
- * @private function of createManagedServer
17
- */
18
- const DEFAULT_AGENT_DIRECTORY_CANDIDATES = [
19
- resolve(process.cwd(), 'agents', 'default'),
20
- resolve(process.cwd(), '..', '..', 'agents', 'default'),
21
- resolve(__dirname, '../../../../../../agents/default'),
22
- ] as const;
23
-
24
7
  /**
25
8
  * Loads default agent books from the repository and persists them into the newly created server.
26
9
  *
@@ -118,70 +101,3 @@ export async function seedServerDefaultAgents(
118
101
  sqlRecorder.addStatement(createInsertStatement(`${input.tablePrefix}AgentHistory`, agentHistoryInsertRecord));
119
102
  }
120
103
  }
121
-
122
- /**
123
- * Loads all default agent books in deterministic filename order.
124
- *
125
- * @returns Sorted default agent sources.
126
- *
127
- * @private function of `seedServerDefaultAgents`
128
- */
129
- async function loadDefaultAgentBooks(): Promise<ReadonlyArray<string_book>> {
130
- const defaultAgentDirectory = await resolveDefaultAgentDirectory();
131
- const directoryEntries = await readdir(defaultAgentDirectory);
132
- const defaultAgentFilenames = directoryEntries
133
- .filter((entry) => entry.toLowerCase().endsWith('.book'))
134
- .sort((leftFilename, rightFilename) => leftFilename.localeCompare(rightFilename));
135
-
136
- return Promise.all(
137
- defaultAgentFilenames.map(async (filename) => {
138
- const filePath = resolve(defaultAgentDirectory, filename);
139
- return (await readFile(filePath, 'utf-8')) as string_book;
140
- }),
141
- );
142
- }
143
-
144
- /**
145
- * Resolves the repository directory that stores default managed-server agents.
146
- *
147
- * @returns Absolute path to `agents/default`.
148
- *
149
- * @private function of `seedServerDefaultAgents`
150
- */
151
- async function resolveDefaultAgentDirectory(): Promise<string> {
152
- for (const directoryCandidate of DEFAULT_AGENT_DIRECTORY_CANDIDATES) {
153
- try {
154
- await access(directoryCandidate);
155
- return directoryCandidate;
156
- } catch {
157
- // Continue to the next candidate directory.
158
- }
159
- }
160
-
161
- throw new DatabaseError(
162
- spaceTrim(
163
- (block) => `
164
- Failed to locate the default Agents Server books directory.
165
-
166
- Checked:
167
- ${block(
168
- DEFAULT_AGENT_DIRECTORY_CANDIDATES.map(
169
- (candidate) => `- \`${normalizePathForLogs(candidate)}\``,
170
- ).join('\n'),
171
- )}
172
- `,
173
- ),
174
- );
175
- }
176
-
177
- /**
178
- * Normalizes path separators for diagnostics.
179
- *
180
- * @param value - Raw filesystem path.
181
- * @returns Slash-normalized path.
182
- *
183
- * @private function of `seedServerDefaultAgents`
184
- */
185
- function normalizePathForLogs(value: string): string {
186
- return value.split('\\').join('/');
187
- }
@@ -2,9 +2,14 @@ import { $getTableName } from '@/src/database/$getTableName';
2
2
  import { $provideSupabaseForServer } from '@/src/database/$provideSupabaseForServer';
3
3
  import type { Json } from '@/src/database/schema';
4
4
  import { FILE_SECURITY_CHECKERS } from '@/src/file-security-checkers';
5
- import { $provideCdnForServer } from '@/src/tools/$provideCdnForServer';
5
+ import {
6
+ $provideCdnForServer,
7
+ isSelfContainedS3StorageSelected,
8
+ resolveCdnPublicUrlForServer,
9
+ } from '@/src/tools/$provideCdnForServer';
6
10
  import { $provideServer } from '@/src/tools/$provideServer';
7
11
  import { getUserFileCdnKey } from '@/src/utils/cdn/utils/getUserFileCdnKey';
12
+ import { resolveFileUploadAvailability } from '@/src/utils/upload/fileUploadAvailability';
8
13
  import { validateMimeType } from '@/src/utils/validators/validateMimeType';
9
14
  import { normalizeChatAttachments } from '@promptbook-local/core';
10
15
  import type { TODO_any } from '@promptbook-local/types';
@@ -231,7 +236,20 @@ async function createShareTargetAttachment(file: File, maxFileUploadBytes: numbe
231
236
 
232
237
  const mimeType = resolveShareTargetMimeType(file.type);
233
238
  const blobPath = getUserFileCdnKey(buffer, normalizedFilename);
234
- const cdn = $provideCdnForServer();
239
+ const providedServer = await $provideServer();
240
+ const fileUploadAvailability = resolveFileUploadAvailability({
241
+ serverId: providedServer.id,
242
+ serverPublicUrl: providedServer.publicUrl,
243
+ isSelfContainedS3StorageSelected: isSelfContainedS3StorageSelected(),
244
+ });
245
+
246
+ if (!fileUploadAvailability.isUploadAvailable) {
247
+ throw new NotAllowed(fileUploadAvailability.message || 'File uploads are not available for this server.');
248
+ }
249
+
250
+ const cdn = $provideCdnForServer({
251
+ cdnPublicUrl: resolveCdnPublicUrlForServer(providedServer.publicUrl),
252
+ });
235
253
  const storageUrl = cdn.getItemUrl(blobPath).href;
236
254
 
237
255
  await cdn.setItem(blobPath, {
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Message shown when self-contained file uploads cannot safely choose a serving domain.
3
+ */
4
+ export const FILE_UPLOAD_REQUIRES_SERVER_DOMAIN_MESSAGE =
5
+ 'Create the first server/domain before uploading files. Uploaded files are served from the current server domain, and raw-IP bootstrap access does not have one yet.';
6
+
7
+ /**
8
+ * Browser-safe upload availability state.
9
+ */
10
+ export type FileUploadAvailability = {
11
+ /**
12
+ * Whether file uploads may be accepted for the current request.
13
+ */
14
+ readonly isUploadAvailable: boolean;
15
+
16
+ /**
17
+ * User-facing reason shown when uploads are unavailable.
18
+ */
19
+ readonly message: string | null;
20
+ };
21
+
22
+ /**
23
+ * Inputs used to resolve whether uploads are allowed for the current server.
24
+ */
25
+ export type ResolveFileUploadAvailabilityOptions = {
26
+ /**
27
+ * Current server id, or `null` before a request is matched to a server/domain.
28
+ */
29
+ readonly serverId: number | null;
30
+
31
+ /**
32
+ * Public URL resolved for the current request.
33
+ */
34
+ readonly serverPublicUrl: URL;
35
+
36
+ /**
37
+ * Whether the deployment is using the bundled self-contained S3 storage.
38
+ */
39
+ readonly isSelfContainedS3StorageSelected: boolean;
40
+ };
41
+
42
+ /**
43
+ * Default upload availability used when no server-side context is available.
44
+ */
45
+ export const AVAILABLE_FILE_UPLOAD: FileUploadAvailability = {
46
+ isUploadAvailable: true,
47
+ message: null,
48
+ };
49
+
50
+ /**
51
+ * Resolves whether file uploads can be accepted for the current request.
52
+ *
53
+ * Self-contained S3 publishes files through the current server domain. While the
54
+ * standalone VPS is still being accessed as a bootstrap/default server, there is
55
+ * no server-specific domain to publish under, so uploads must be disabled.
56
+ *
57
+ * @param options - Current server and storage mode.
58
+ * @returns Browser-safe upload availability state.
59
+ */
60
+ export function resolveFileUploadAvailability(
61
+ options: ResolveFileUploadAvailabilityOptions,
62
+ ): FileUploadAvailability {
63
+ if (!options.isSelfContainedS3StorageSelected) {
64
+ return AVAILABLE_FILE_UPLOAD;
65
+ }
66
+
67
+ if (options.serverId !== null) {
68
+ return AVAILABLE_FILE_UPLOAD;
69
+ }
70
+
71
+ if (isLocalDevelopmentUrl(options.serverPublicUrl)) {
72
+ return AVAILABLE_FILE_UPLOAD;
73
+ }
74
+
75
+ return {
76
+ isUploadAvailable: false,
77
+ message: FILE_UPLOAD_REQUIRES_SERVER_DOMAIN_MESSAGE,
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Checks whether a server URL points to local development.
83
+ *
84
+ * @param serverPublicUrl - Public URL resolved for the current request.
85
+ * @returns `true` for localhost and loopback hosts.
86
+ */
87
+ function isLocalDevelopmentUrl(serverPublicUrl: URL): boolean {
88
+ const hostname = serverPublicUrl.hostname.toLowerCase();
89
+
90
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
91
+ }