@promptbook/cli 0.112.0-104 → 0.112.0-106

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 (174) hide show
  1. package/apps/agents-server/src/app/admin/image-generator-test/ImageAttachmentsEditor.tsx +19 -3
  2. package/apps/agents-server/src/app/admin/limits/LimitsClient.tsx +11 -12
  3. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +34 -2
  4. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +6 -1
  5. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +13 -1
  6. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +11 -2
  7. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +11 -1
  8. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +7 -1
  9. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +11 -1
  10. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +6 -2
  11. package/apps/agents-server/src/app/api/images/[filename]/route.ts +6 -2
  12. package/apps/agents-server/src/app/api/internal/agent-runner-limits/route.ts +51 -0
  13. package/apps/agents-server/src/app/api/upload/route.ts +48 -12
  14. package/apps/agents-server/src/app/layout.tsx +13 -0
  15. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadAvailabilityContext.tsx +50 -0
  16. package/apps/agents-server/src/components/FileUploadAvailability/FileUploadUnavailableNotice.tsx +45 -0
  17. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +85 -76
  18. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +7 -3
  19. package/apps/agents-server/src/components/NewAgentDialog/NewAgentWizardKnowledgeStep.tsx +6 -0
  20. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentWizardKnowledgeState.ts +8 -1
  21. package/apps/agents-server/src/constants/serverLimits.ts +22 -2
  22. package/apps/agents-server/src/database/seedDefaultAgents.ts +218 -0
  23. package/apps/agents-server/src/tools/$provideCdnForServer.ts +72 -8
  24. package/apps/agents-server/src/utils/defaultAgents/loadDefaultAgentBooks.ts +103 -0
  25. package/apps/agents-server/src/utils/knowledge/createInlineKnowledgeSourceUploader.ts +24 -5
  26. package/apps/agents-server/src/utils/serverLimits.ts +26 -1
  27. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +1 -85
  28. package/apps/agents-server/src/utils/shareTargetPayloads.ts +20 -2
  29. package/apps/agents-server/src/utils/upload/fileUploadAvailability.ts +91 -0
  30. package/apps/agents-server/src/utils/upload/uploadFileToServer.ts +46 -2
  31. package/esm/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  32. package/esm/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  33. package/esm/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  34. package/esm/index.es.js +602 -39
  35. package/esm/index.es.js.map +1 -1
  36. package/esm/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  37. package/esm/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  38. package/esm/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  39. package/esm/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  40. package/esm/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  41. package/esm/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  42. package/esm/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  43. package/esm/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  44. package/esm/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  45. package/esm/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  46. package/esm/src/version.d.ts +1 -1
  47. package/package.json +1 -1
  48. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +28 -29
  49. package/src/book-components/BookEditor/BookEditor.tsx +10 -7
  50. package/src/book-components/BookEditor/BookEditorMonaco.tsx +3 -1
  51. package/src/book-components/BookEditor/BookEditorTheme.ts +32 -0
  52. package/src/book-components/BookEditor/useBookEditorMonacoLanguage.ts +15 -15
  53. package/src/book-components/BookEditor/useBookEditorMonacoLifecycle.ts +1 -5
  54. package/src/book-components/BookEditor/useBookEditorMonacoStyles.ts +2 -1
  55. package/src/cli/cli-commands/agent-folder/agentProjectPaths.ts +7 -0
  56. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +109 -9
  57. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +132 -4
  58. package/src/other/templates/getTemplatesPipelineCollection.ts +874 -760
  59. package/src/version.ts +2 -2
  60. package/src/versions.txt +2 -0
  61. package/umd/apps/agents-server/src/constants/federatedAgentImport.d.ts +42 -0
  62. package/umd/apps/agents-server/src/constants/serverLimits.d.ts +207 -0
  63. package/umd/apps/agents-server/src/constants/toolUsageLimits.d.ts +55 -0
  64. package/umd/index.umd.js +602 -39
  65. package/umd/index.umd.js.map +1 -1
  66. package/umd/scripts/run-agent-messages/main/AgentMessageFailureTracker.d.ts +27 -0
  67. package/umd/scripts/run-agent-messages/main/handleAgentWatchError.d.ts +4 -0
  68. package/umd/scripts/run-agent-messages/main/runAgentMessages.d.ts +1 -0
  69. package/umd/scripts/run-agent-messages/messages/moveAgentMessageToFailed.d.ts +17 -0
  70. package/umd/src/book-components/BookEditor/BookEditor.d.ts +5 -4
  71. package/umd/src/book-components/BookEditor/BookEditorTheme.d.ts +24 -0
  72. package/umd/src/book-components/BookEditor/useBookEditorMonacoLanguage.d.ts +1 -6
  73. package/umd/src/book-components/BookEditor/useBookEditorMonacoLifecycle.d.ts +1 -4
  74. package/umd/src/book-components/BookEditor/useBookEditorMonacoStyles.d.ts +2 -1
  75. package/umd/src/cli/cli-commands/agent-folder/agentProjectPaths.d.ts +6 -0
  76. package/umd/src/version.d.ts +1 -1
  77. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +0 -108
  78. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +0 -117
  79. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +0 -119
  80. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +0 -74
  81. package/apps/agents-server/src/utils/defaultAgents/defaultAgents.ts +0 -168
  82. package/apps/agents-server/src/utils/defaultAgents/installDefaultAgents.ts +0 -139
  83. package/apps/agents-server/tests/e2e/authentication-and-navigation.spec.ts.todo +0 -178
  84. package/src/_packages/browser.index.ts +0 -31
  85. package/src/_packages/browser.readme.md +0 -43
  86. package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.test.ts.todo +0 -265
  87. package/src/book-components/BookEditor/BookEditorMonaco.test.tsx.todo +0 -115
  88. package/src/book-components/Chat/utils/renderMarkdown.test.ts.tmp +0 -199
  89. package/src/collection/agent-collection/constructors/agent-collection-in-directory/AgentCollectionInDirectory.test.ts.todo +0 -131
  90. package/src/commands/_common/parseCommand.test.ts.todo +0 -48
  91. package/src/commitments/META_LINK/META_LINK.test.ts.todo +0 -75
  92. package/src/conversion/validation/pipelineStringToJson-errors.test.ts.todo +0 -33
  93. package/src/dialogs/simple-prompt/SimplePromptInterfaceTools.ts +0 -51
  94. package/src/executables/browsers/locateSafari.test.ts.tmp +0 -15
  95. package/src/execution/PromptbookFetch.test-type.ts +0 -14
  96. package/src/execution/createPipelineExecutor/00-createPipelineExecutor.test.ts.todo +0 -0
  97. package/src/execution/execution-report/executionReportJsonToString.test.ts.todo +0 -83
  98. package/src/execution/utils/usageToHuman.test.ts.todo +0 -80
  99. package/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.ts +0 -76
  100. package/src/llm-providers/_common/utils/assertUniqueModels.ts +0 -27
  101. package/src/llm-providers/_multiple/playground/playground.ts +0 -141
  102. package/src/llm-providers/_multiple/playground/tsconfig.json +0 -19
  103. package/src/llm-providers/agent/playground/playground.ts +0 -190
  104. package/src/llm-providers/agent/playground/tsconfig.json +0 -19
  105. package/src/llm-providers/anthropic-claude/playground/playground.ts +0 -99
  106. package/src/llm-providers/anthropic-claude/playground/tsconfig.json +0 -19
  107. package/src/llm-providers/azure-openai/playground/playground.ts +0 -101
  108. package/src/llm-providers/azure-openai/playground/tsconfig.json +0 -19
  109. package/src/llm-providers/ollama/playground/playground.ts +0 -120
  110. package/src/llm-providers/ollama/playground/tsconfig.json +0 -19
  111. package/src/llm-providers/openai/playground/playground.ts +0 -406
  112. package/src/llm-providers/openai/playground/tsconfig.json +0 -19
  113. package/src/llm-providers/remote/playground/playground.ts +0 -144
  114. package/src/llm-providers/remote/playground/tsconfig.json +0 -19
  115. package/src/llm-providers/vercel/playground/playground.ts +0 -133
  116. package/src/llm-providers/vercel/playground/tsconfig.json +0 -19
  117. package/src/personas/preparePersona.test.ts.todo +0 -126
  118. package/src/playground/backup/_playground-boilerplate.ts.txt +0 -37
  119. package/src/playground/backup/playground-agent-os.txt +0 -62
  120. package/src/playground/backup/playground-brj-app.ts.txt +0 -302
  121. package/src/playground/backup/playground-browser-playwright.txt +0 -110
  122. package/src/playground/backup/playground-claude-mcp.txt +0 -43
  123. package/src/playground/backup/playground-document-conversion.txt +0 -84
  124. package/src/playground/backup/playground-glob.ts.txt +0 -42
  125. package/src/playground/backup/playground-mcp-server.txt +0 -1
  126. package/src/playground/backup/playground-openai-agent-kit.txt +0 -73
  127. package/src/playground/backup/playground-openai-function-calling.txt +0 -131
  128. package/src/playground/backup/playground-openai-streaming.ts.txt +0 -68
  129. package/src/playground/backup/playground-scrape-knowledge.txt +0 -65
  130. package/src/playground/backup/playground-scraperFetch.ts.txt +0 -44
  131. package/src/playground/backup/playground-using-openai-compatible-route-on-agents-server.ts.txt +0 -49
  132. package/src/playground/backup/playground-write-pavolhejny-bio.txt +0 -120
  133. package/src/playground/permanent/_boilerplate.ts +0 -54
  134. package/src/playground/permanent/agent-with-browser-playground.ts +0 -92
  135. package/src/playground/permanent/error-handling-playground.ts +0 -103
  136. package/src/playground/playground.ts +0 -36
  137. package/src/playground/tsconfig.json +0 -19
  138. package/src/scrapers/_boilerplate/BoilerplateScraper.test.ts.todo +0 -73
  139. package/src/scrapers/_boilerplate/playground/boilerplate-scraper-playground.ts +0 -79
  140. package/src/scrapers/_boilerplate/playground/tsconfig.json +0 -19
  141. package/src/scrapers/_common/utils/files/blobToDataurl.test.ts.todo +0 -17
  142. package/src/scrapers/_common/utils/files/dataurlToBlob.test.ts.todo +0 -52
  143. package/src/scrapers/_common/utils/files/isValidDataurl.test.ts.todo +0 -42
  144. package/src/scrapers/_common/utils/files/shorten.test.ts.todo +0 -13
  145. package/src/scrapers/document/playground/document-scraper-playground.ts +0 -80
  146. package/src/scrapers/document/playground/tsconfig.json +0 -19
  147. package/src/scrapers/document-legacy/playground/legacy-document-scraper-playground.ts +0 -80
  148. package/src/scrapers/document-legacy/playground/tsconfig.json +0 -19
  149. package/src/scrapers/markdown/playground/markdown-scraper-playground.ts +0 -74
  150. package/src/scrapers/markdown/playground/tsconfig.json +0 -19
  151. package/src/scrapers/markitdown/MarkitdownScraper.test.ts.todo +0 -132
  152. package/src/scrapers/markitdown/playground/markitdown-scraper-playground.ts +0 -91
  153. package/src/scrapers/markitdown/playground/tsconfig.json +0 -19
  154. package/src/scrapers/pdf/PdfScraper.test.ts.todo +0 -52
  155. package/src/scrapers/pdf/playground/pdf-scraper-playground.ts +0 -75
  156. package/src/scrapers/pdf/playground/tsconfig.json +0 -19
  157. package/src/scrapers/website/playground/tsconfig.json +0 -19
  158. package/src/scrapers/website/playground/website-scraper-playground.ts +0 -82
  159. package/src/storage/_common/PromptbookStorage.test-type.ts +0 -14
  160. package/src/storage/local-storage/getIndexedDbStorage.ts +0 -36
  161. package/src/storage/local-storage/getLocalStorage.ts +0 -33
  162. package/src/storage/local-storage/getSessionStorage.ts +0 -33
  163. package/src/storage/local-storage/utils/IndexedDbStorageOptions.ts +0 -16
  164. package/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.ts +0 -58
  165. package/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.ts +0 -45
  166. package/src/transpilers/formatted-book-in-markdown/FormattedBookInMarkdownTranspiler.test.ts.todo +0 -35
  167. package/src/transpilers/openai-sdk/playground/playground.ts +0 -85
  168. package/src/transpilers/openai-sdk/playground/tmp/chatbot-openaisdk-1.js +0 -194
  169. package/src/transpilers/openai-sdk/playground/tmp/package.json +0 -3
  170. package/src/transpilers/openai-sdk/playground/tsconfig.json +0 -18
  171. package/src/utils/editable/utils/findUsableParameters.test.ts.todo +0 -43
  172. package/src/utils/editable/utils/stringifyPipelineJson.test.ts.todo +0 -38
  173. package/src/utils/markdown/prettifyMarkdown.test.ts.tmp +0 -42
  174. package/src/utils/serialization/serializeToPromptbookJavascript.test.ts.todo +0 -116
@@ -3,15 +3,22 @@ import { NextRequest, NextResponse } from 'next/server';
3
3
  import { spaceTrim } from 'spacetrim';
4
4
  import { assertsError } from '../../../../../../src/errors/assertsError';
5
5
  import { LimitReachedError } from '../../../../../../src/errors/LimitReachedError';
6
+ import { NotAllowed } from '../../../../../../src/errors/NotAllowed';
6
7
  import { UnexpectedError } from '../../../../../../src/errors/UnexpectedError';
7
8
  import { $getTableName } from '../../../database/$getTableName';
8
9
  import { $provideSupabase } from '../../../database/$provideSupabase';
9
10
  import type { AgentsServerDatabase } from '../../../database/schema';
10
- import { $provideCdnForServer } from '../../../tools/$provideCdnForServer';
11
+ import {
12
+ $provideCdnForServer,
13
+ isSelfContainedS3StorageSelected,
14
+ resolveCdnPublicUrlForServer,
15
+ } from '../../../tools/$provideCdnForServer';
16
+ import { $provideServer } from '../../../tools/$provideServer';
11
17
  import { getSafeCdnPath } from '../../../utils/cdn/utils/getSafeCdnPath';
12
18
  import { FILE_SECURITY_CHECKERS } from '../../../file-security-checkers';
13
19
  import { getUserIdFromRequest } from '../../../utils/getUserIdFromRequest';
14
20
  import { getMaxFileUploadSizeBytes } from '../../../utils/serverLimits';
21
+ import { resolveFileUploadAvailability } from '../../../utils/upload/fileUploadAvailability';
15
22
  import { validateMimeType } from '../../../utils/validators/validateMimeType';
16
23
 
17
24
  /**
@@ -47,6 +54,13 @@ type ParsedUploadRequest = {
47
54
  contentType: string;
48
55
  };
49
56
 
57
+ /**
58
+ * Server context returned by `$provideServer`.
59
+ *
60
+ * @private
61
+ */
62
+ type ProvidedServer = Awaited<ReturnType<typeof $provideServer>>;
63
+
50
64
  /**
51
65
  * Normalizes upload purpose to a non-empty string.
52
66
  *
@@ -182,11 +196,33 @@ async function updateUploadedFileSecurityResult(
182
196
  }
183
197
  }
184
198
 
199
+ /**
200
+ * Ensures the current server/domain can accept file uploads.
201
+ *
202
+ * @param providedServer - Current server routing context.
203
+ * @throws `NotAllowed` when uploads would be published without a server domain.
204
+ * @private
205
+ */
206
+ function assertFileUploadAvailable(providedServer: ProvidedServer): void {
207
+ const fileUploadAvailability = resolveFileUploadAvailability({
208
+ serverId: providedServer.id,
209
+ serverPublicUrl: providedServer.publicUrl,
210
+ isSelfContainedS3StorageSelected: isSelfContainedS3StorageSelected(),
211
+ });
212
+
213
+ if (!fileUploadAvailability.isUploadAvailable) {
214
+ throw new NotAllowed(fileUploadAvailability.message || 'File uploads are not available for this server.');
215
+ }
216
+ }
217
+
185
218
  /**
186
219
  * Handles file upload requests.
187
220
  */
188
221
  export async function POST(request: NextRequest) {
189
222
  try {
223
+ const providedServer = await $provideServer();
224
+ assertFileUploadAvailable(providedServer);
225
+
190
226
  const { file, pathname, purpose, contentType } = await parseUploadRequest(request);
191
227
  const fileBuffer = Buffer.from(await file.arrayBuffer());
192
228
  const maxFileSize = await getMaxFileUploadSizeBytes();
@@ -201,7 +237,9 @@ export async function POST(request: NextRequest) {
201
237
  );
202
238
  }
203
239
 
204
- const cdn = $provideCdnForServer();
240
+ const cdn = $provideCdnForServer({
241
+ cdnPublicUrl: resolveCdnPublicUrlForServer(providedServer.publicUrl),
242
+ });
205
243
  const storageUrl = cdn.getItemUrl(pathname).href;
206
244
  const userId = await getUserIdFromRequest(request);
207
245
 
@@ -227,17 +265,15 @@ export async function POST(request: NextRequest) {
227
265
 
228
266
  console.error('Upload failed:', error);
229
267
 
230
- return new Response(
231
- JSON.stringify(
232
- serializeError(error),
233
- // <- TODO: [🐱‍🚀] Rename `serializeError` to `errorToJson`
234
- null,
235
- 4,
236
- // <- TODO: [🐱‍🚀] Allow to configure pretty print for agent server
237
- ),
268
+ const serializedError = serializeError(error);
269
+
270
+ return NextResponse.json(
271
+ {
272
+ ...serializedError,
273
+ error: serializedError.message,
274
+ },
238
275
  {
239
- status: 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
240
- headers: { 'Content-Type': 'application/json' },
276
+ status: error instanceof NotAllowed ? 403 : 400, // <- TODO: [🐱‍🚀] Make `errorToHttpStatusCode`
241
277
  },
242
278
  );
243
279
  }
@@ -16,6 +16,7 @@ import {
16
16
  SERVER_LANGUAGE_METADATA_KEY,
17
17
  } from '../languages/ServerLanguageRegistry';
18
18
  import { $provideServer } from '../tools/$provideServer';
19
+ import { isSelfContainedS3StorageSelected } from '../tools/$provideCdnForServer';
19
20
  import { loadAgentOrganizationState } from '../utils/agentOrganization/loadAgentOrganizationState';
20
21
  import type { AgentOrganizationAgent, AgentOrganizationFolder } from '../utils/agentOrganization/types';
21
22
  import { getAgentNaming } from '../utils/getAgentNaming';
@@ -50,6 +51,7 @@ import {
50
51
  SHIBBOLETH_AUTHENTICATION_METADATA_KEYS,
51
52
  resolveShibbolethAuthenticationMenuStatus,
52
53
  } from '../constants/shibbolethAuth';
54
+ import { resolveFileUploadAvailability } from '../utils/upload/fileUploadAvailability';
53
55
  import '@prisma/studio-core/ui/index.css';
54
56
  import './globals.css';
55
57
 
@@ -267,6 +269,7 @@ export default async function RootLayout({
267
269
  const currentUserPromise = getCurrentUser();
268
270
  const isAdminPromise = isUserAdmin();
269
271
  const isGlobalAdminPromise = isUserGlobalAdmin();
272
+ const providedServerPromise = $provideServer();
270
273
  const serverVisibilityPromise = getServerVisibility();
271
274
  const agentNamingPromise = getAgentNaming();
272
275
  const organizationStatePromise = isAdminPromise.then((isAdmin) =>
@@ -274,6 +277,13 @@ export default async function RootLayout({
274
277
  );
275
278
  const chatPreferencesPromise = getDefaultChatPreferences();
276
279
  const defaultIsNotificationsOnPromise = getDefaultIsNotificationsOn();
280
+ const fileUploadAvailabilityPromise = providedServerPromise.then((providedServer) =>
281
+ resolveFileUploadAvailability({
282
+ serverId: providedServer.id,
283
+ serverPublicUrl: providedServer.publicUrl,
284
+ isSelfContainedS3StorageSelected: isSelfContainedS3StorageSelected(),
285
+ }),
286
+ );
277
287
  const customStylesheetCssPromise = resolveOptionalLayoutText(
278
288
  'custom stylesheet CSS',
279
289
  getAggregatedCustomStylesheetCss,
@@ -340,6 +350,7 @@ export default async function RootLayout({
340
350
  federatedServers,
341
351
  footerLinks,
342
352
  serverVisibility,
353
+ fileUploadAvailability,
343
354
  ] = await Promise.all([
344
355
  isAdminPromise,
345
356
  isGlobalAdminPromise,
@@ -356,6 +367,7 @@ export default async function RootLayout({
356
367
  federatedServersPromise,
357
368
  footerLinksPromise,
358
369
  serverVisibilityPromise,
370
+ fileUploadAvailabilityPromise,
359
371
  ]);
360
372
 
361
373
  const serverName = layoutMetadata.SERVER_NAME || 'Promptbook Agents Server';
@@ -429,6 +441,7 @@ export default async function RootLayout({
429
441
  defaultChatVisualMode={defaultChatVisualMode}
430
442
  defaultAgentAvatarVisualId={defaultAgentAvatarVisualId}
431
443
  webPushPublicKey={webPushPublicKey}
444
+ fileUploadAvailability={fileUploadAvailability}
432
445
  >
433
446
  {children}
434
447
  </LayoutWrapper>
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, type ReactNode } from 'react';
4
+ import {
5
+ AVAILABLE_FILE_UPLOAD,
6
+ type FileUploadAvailability,
7
+ } from '../../utils/upload/fileUploadAvailability';
8
+
9
+ /**
10
+ * Context carrying file upload availability for the current server/domain.
11
+ */
12
+ const FileUploadAvailabilityContext = createContext<FileUploadAvailability>(AVAILABLE_FILE_UPLOAD);
13
+
14
+ /**
15
+ * Props consumed by `FileUploadAvailabilityProvider`.
16
+ */
17
+ type FileUploadAvailabilityProviderProps = {
18
+ /**
19
+ * Current upload availability state.
20
+ */
21
+ readonly value: FileUploadAvailability;
22
+
23
+ /**
24
+ * Nested app content.
25
+ */
26
+ readonly children: ReactNode;
27
+ };
28
+
29
+ /**
30
+ * Provides file upload availability to upload controls.
31
+ *
32
+ * @param props - Provider props.
33
+ * @returns Provider-wrapped children.
34
+ */
35
+ export function FileUploadAvailabilityProvider(props: FileUploadAvailabilityProviderProps) {
36
+ return (
37
+ <FileUploadAvailabilityContext.Provider value={props.value}>
38
+ {props.children}
39
+ </FileUploadAvailabilityContext.Provider>
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Reads the current file upload availability state.
45
+ *
46
+ * @returns Current upload availability state.
47
+ */
48
+ export function useFileUploadAvailability(): FileUploadAvailability {
49
+ return useContext(FileUploadAvailabilityContext);
50
+ }
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+
3
+ import { AlertTriangle } from 'lucide-react';
4
+ import Link from 'next/link';
5
+ import { useFileUploadAvailability } from './FileUploadAvailabilityContext';
6
+
7
+ /**
8
+ * Props consumed by `FileUploadUnavailableNotice`.
9
+ */
10
+ type FileUploadUnavailableNoticeProps = {
11
+ /**
12
+ * Optional classes appended to the notice root.
13
+ */
14
+ readonly className?: string;
15
+ };
16
+
17
+ /**
18
+ * Shows a shared notice when file uploads are disabled for the current server.
19
+ *
20
+ * @param props - Notice props.
21
+ * @returns Upload notice or `null` when uploads are available.
22
+ */
23
+ export function FileUploadUnavailableNotice({ className }: FileUploadUnavailableNoticeProps) {
24
+ const fileUploadAvailability = useFileUploadAvailability();
25
+
26
+ if (fileUploadAvailability.isUploadAvailable || !fileUploadAvailability.message) {
27
+ return null;
28
+ }
29
+
30
+ return (
31
+ <div
32
+ className={`flex items-start gap-3 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900 ${
33
+ className || ''
34
+ }`}
35
+ >
36
+ <AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0" aria-hidden="true" />
37
+ <div className="space-y-1">
38
+ <p className="font-medium">{fileUploadAvailability.message}</p>
39
+ <Link href="/admin/servers" className="inline-flex font-semibold underline underline-offset-2">
40
+ Open server/domain settings
41
+ </Link>
42
+ </div>
43
+ </div>
44
+ );
45
+ }
@@ -16,6 +16,7 @@ import { ChatVisualModeProvider } from '../ChatVisualMode/ChatVisualModeProvider
16
16
  import { ClientVersionMismatchListener } from '../ClientVersion/ClientVersionMismatchListener';
17
17
  import { ChatEnterBehaviorPreferencesProvider } from '../ChatEnterBehavior/ChatEnterBehaviorPreferencesProvider';
18
18
  import { HomepageOptimisticNavigation } from '../Homepage/HomepageOptimisticNavigation';
19
+ import { FileUploadAvailabilityProvider } from '../FileUploadAvailability/FileUploadAvailabilityContext';
19
20
  import { Footer, type FooterLink } from '../Footer/Footer';
20
21
  import { Header } from '../Header/Header';
21
22
  import { MobileMenuHoistingProvider } from '../Header/MobileMenuHoistingContext';
@@ -30,6 +31,7 @@ import { SoundSystemProvider } from '../SoundSystemProvider/SoundSystemProvider'
30
31
  import { ThemeModeProvider } from '../ThemeMode/ThemeModeProvider';
31
32
  import { ViewportHeightController } from '../ViewportHeightController/ViewportHeightController';
32
33
  import type { ControlPanelOptionAvailability } from '../../utils/getControlPanelOptionAvailability';
34
+ import type { FileUploadAvailability } from '../../utils/upload/fileUploadAvailability';
33
35
 
34
36
  /**
35
37
  * Props for layout wrapper.
@@ -73,6 +75,10 @@ type LayoutWrapperProps = {
73
75
  defaultChatVisualMode: string;
74
76
  defaultAgentAvatarVisualId: AvatarVisualId;
75
77
  webPushPublicKey: string | null;
78
+ /**
79
+ * Current file upload availability for the active server/domain.
80
+ */
81
+ fileUploadAvailability: FileUploadAvailability;
76
82
  };
77
83
 
78
84
  /**
@@ -105,6 +111,7 @@ export function LayoutWrapper({
105
111
  defaultChatVisualMode,
106
112
  defaultAgentAvatarVisualId,
107
113
  webPushPublicKey,
114
+ fileUploadAvailability,
108
115
  }: LayoutWrapperProps) {
109
116
  const pathname = usePathname();
110
117
  const searchParams = useSearchParams();
@@ -127,83 +134,85 @@ export function LayoutWrapper({
127
134
  defaultLanguage={defaultServerLanguage}
128
135
  isServerLanguageEnforced={isServerLanguageEnforced}
129
136
  >
130
- <DefaultAgentAvatarVisualProvider defaultAgentAvatarVisualId={defaultAgentAvatarVisualId}>
131
- <ThemeModeProvider defaultThemeMode={defaultThemeMode}>
132
- <ChatVisualModeProvider defaultChatVisualMode={defaultChatVisualMode}>
133
- <AsyncDialogsProvider>
134
- <AgentNamingProvider naming={agentNaming}>
135
- <LegacyUiAutoTranslator />
136
- <PrivateModePreferencesProvider>
137
- <SelfLearningPreferencesProvider>
138
- <SoundSystemProvider
139
- initialIsSoundsOn={defaultIsSoundsOn}
140
- initialIsVibrationOn={defaultIsVibrationOn}
141
- >
142
- <NotificationsProvider>
143
- <BrowserPushNotificationsProvider
144
- defaultEnabled={defaultIsNotificationsOn}
145
- pushPublicKey={webPushPublicKey}
146
- isMetadataAvailable={controlPanelOptionAvailability.notifications}
147
- >
148
- <ChatEnterBehaviorPreferencesProvider>
149
- <ClientVersionMismatchListener />
150
- <ViewportHeightController />
151
- <NavigationProgressBar />
152
- <MenuHoistingProvider>
153
- <MobileMenuHoistingProvider>
154
- <MetadataFlagsProvider
155
- value={{
156
- isExperimentalPwaAppEnabled,
157
- controlPanelOptionAvailability,
158
- }}
159
- >
160
- {shouldRenderMinimalShell ? (
161
- <main className={minimalMainClassName}>
162
- {children}
163
- </main>
164
- ) : (
165
- <div className="agents-server-app-shell flex flex-col">
166
- <Header
167
- isAdmin={isAdmin}
168
- isGlobalAdmin={isGlobalAdmin}
169
- currentUser={currentUser}
170
- serverName={serverName}
171
- serverLogoUrl={serverLogoUrl}
172
- agents={agents}
173
- agentFolders={agentFolders}
174
- federatedServers={federatedServers}
175
- isExperimental={isExperimental}
176
- feedbackMode={feedbackMode}
177
- shibbolethAuthenticationStatus={
178
- shibbolethAuthenticationStatus
179
- }
180
- />
181
- <main className={mainClassName}>
182
- <HomepageOptimisticNavigation
183
- pathname={pathname}
184
- >
185
- {children}
186
- </HomepageOptimisticNavigation>
137
+ <FileUploadAvailabilityProvider value={fileUploadAvailability}>
138
+ <DefaultAgentAvatarVisualProvider defaultAgentAvatarVisualId={defaultAgentAvatarVisualId}>
139
+ <ThemeModeProvider defaultThemeMode={defaultThemeMode}>
140
+ <ChatVisualModeProvider defaultChatVisualMode={defaultChatVisualMode}>
141
+ <AsyncDialogsProvider>
142
+ <AgentNamingProvider naming={agentNaming}>
143
+ <LegacyUiAutoTranslator />
144
+ <PrivateModePreferencesProvider>
145
+ <SelfLearningPreferencesProvider>
146
+ <SoundSystemProvider
147
+ initialIsSoundsOn={defaultIsSoundsOn}
148
+ initialIsVibrationOn={defaultIsVibrationOn}
149
+ >
150
+ <NotificationsProvider>
151
+ <BrowserPushNotificationsProvider
152
+ defaultEnabled={defaultIsNotificationsOn}
153
+ pushPublicKey={webPushPublicKey}
154
+ isMetadataAvailable={controlPanelOptionAvailability.notifications}
155
+ >
156
+ <ChatEnterBehaviorPreferencesProvider>
157
+ <ClientVersionMismatchListener />
158
+ <ViewportHeightController />
159
+ <NavigationProgressBar />
160
+ <MenuHoistingProvider>
161
+ <MobileMenuHoistingProvider>
162
+ <MetadataFlagsProvider
163
+ value={{
164
+ isExperimentalPwaAppEnabled,
165
+ controlPanelOptionAvailability,
166
+ }}
167
+ >
168
+ {shouldRenderMinimalShell ? (
169
+ <main className={minimalMainClassName}>
170
+ {children}
187
171
  </main>
188
- {isFooterShown && !isFooterHiddenOnPage && (
189
- <Footer extraLinks={footerLinks} />
190
- )}
191
- </div>
192
- )}
193
- </MetadataFlagsProvider>
194
- </MobileMenuHoistingProvider>
195
- </MenuHoistingProvider>
196
- </ChatEnterBehaviorPreferencesProvider>
197
- </BrowserPushNotificationsProvider>
198
- </NotificationsProvider>
199
- </SoundSystemProvider>
200
- </SelfLearningPreferencesProvider>
201
- </PrivateModePreferencesProvider>
202
- </AgentNamingProvider>
203
- </AsyncDialogsProvider>
204
- </ChatVisualModeProvider>
205
- </ThemeModeProvider>
206
- </DefaultAgentAvatarVisualProvider>
172
+ ) : (
173
+ <div className="agents-server-app-shell flex flex-col">
174
+ <Header
175
+ isAdmin={isAdmin}
176
+ isGlobalAdmin={isGlobalAdmin}
177
+ currentUser={currentUser}
178
+ serverName={serverName}
179
+ serverLogoUrl={serverLogoUrl}
180
+ agents={agents}
181
+ agentFolders={agentFolders}
182
+ federatedServers={federatedServers}
183
+ isExperimental={isExperimental}
184
+ feedbackMode={feedbackMode}
185
+ shibbolethAuthenticationStatus={
186
+ shibbolethAuthenticationStatus
187
+ }
188
+ />
189
+ <main className={mainClassName}>
190
+ <HomepageOptimisticNavigation
191
+ pathname={pathname}
192
+ >
193
+ {children}
194
+ </HomepageOptimisticNavigation>
195
+ </main>
196
+ {isFooterShown && !isFooterHiddenOnPage && (
197
+ <Footer extraLinks={footerLinks} />
198
+ )}
199
+ </div>
200
+ )}
201
+ </MetadataFlagsProvider>
202
+ </MobileMenuHoistingProvider>
203
+ </MenuHoistingProvider>
204
+ </ChatEnterBehaviorPreferencesProvider>
205
+ </BrowserPushNotificationsProvider>
206
+ </NotificationsProvider>
207
+ </SoundSystemProvider>
208
+ </SelfLearningPreferencesProvider>
209
+ </PrivateModePreferencesProvider>
210
+ </AgentNamingProvider>
211
+ </AsyncDialogsProvider>
212
+ </ChatVisualModeProvider>
213
+ </ThemeModeProvider>
214
+ </DefaultAgentAvatarVisualProvider>
215
+ </FileUploadAvailabilityProvider>
207
216
  </ServerLanguageProvider>
208
217
  );
209
218
  }
@@ -6,6 +6,8 @@ import { useCallback, useState } from 'react';
6
6
  import { BookEditor } from '../../../../../src/book-components/BookEditor/BookEditor';
7
7
  import { bookEditorUploadHandler } from '../../utils/upload/createBookEditorUploadHandler';
8
8
  import { useAgentNaming } from '../AgentNaming/AgentNamingContext';
9
+ import { FileUploadUnavailableNotice } from '../FileUploadAvailability/FileUploadUnavailableNotice';
10
+ import { useFileUploadAvailability } from '../FileUploadAvailability/FileUploadAvailabilityContext';
9
11
  import { Dialog } from '../Portal/Dialog';
10
12
  import { useServerLanguage } from '../ServerLanguage/ServerLanguageProvider';
11
13
  import { usePromptbookTheme } from '../ThemeMode/usePromptbookTheme';
@@ -29,6 +31,7 @@ export function NewAgentDialog(props: NewAgentDialogProps) {
29
31
  const [isCreating, setIsCreating] = useState(false);
30
32
  const { formatText } = useAgentNaming();
31
33
  const { t } = useServerLanguage();
34
+ const fileUploadAvailability = useFileUploadAvailability();
32
35
  const { promptbookTheme } = usePromptbookTheme();
33
36
  const { requestClose } = useDirtyModalGuard({
34
37
  hasUnsavedChanges: agentSource !== initialAgentSource,
@@ -79,9 +82,10 @@ export function NewAgentDialog(props: NewAgentDialogProps) {
79
82
  </div>
80
83
 
81
84
  <div
82
- className="relative flex-1 overflow-hidden bg-slate-50/60 p-4 dark:bg-slate-950/55" /* [✨🧬] onDragEnter={() => setIsInteracted(true)} */
85
+ className="relative flex flex-1 flex-col overflow-hidden bg-slate-50/60 p-4 dark:bg-slate-950/55" /* [✨🧬] onDragEnter={() => setIsInteracted(true)} */
83
86
  >
84
- <div className="h-full overflow-hidden rounded-2xl border border-slate-200/80 shadow-inner dark:border-slate-800/80">
87
+ {!fileUploadAvailability.isUploadAvailable && <FileUploadUnavailableNotice className="mb-3" />}
88
+ <div className="min-h-0 flex-1 overflow-hidden rounded-2xl border border-slate-200/80 shadow-inner dark:border-slate-800/80">
85
89
  <BookEditor
86
90
  className="h-full w-full"
87
91
  agentSource={agentSource}
@@ -92,7 +96,7 @@ export function NewAgentDialog(props: NewAgentDialogProps) {
92
96
  height="100%"
93
97
  isBorderRadiusDisabled
94
98
  isVerbose={false}
95
- onFileUpload={bookEditorUploadHandler}
99
+ onFileUpload={fileUploadAvailability.isUploadAvailable ? bookEditorUploadHandler : undefined}
96
100
  theme={promptbookTheme}
97
101
  />
98
102
  </div>
@@ -1,5 +1,7 @@
1
1
  import { Upload } from 'lucide-react';
2
2
  import type { ChangeEvent, Dispatch, KeyboardEvent, RefObject, SetStateAction } from 'react';
3
+ import { FileUploadUnavailableNotice } from '../FileUploadAvailability/FileUploadUnavailableNotice';
4
+ import { useFileUploadAvailability } from '../FileUploadAvailability/FileUploadAvailabilityContext';
3
5
  import { NewAgentWizardClassNames } from './NewAgentWizardClassNames';
4
6
  import type { NewAgentWizardState } from './NewAgentWizardState';
5
7
  import type { NewAgentWizardTranslate } from './NewAgentWizardTranslate';
@@ -58,6 +60,7 @@ type NewAgentWizardKnowledgeStepProps = {
58
60
  * @private internal component of <NewAgentWizard/>.
59
61
  */
60
62
  export function NewAgentWizardKnowledgeStep(props: NewAgentWizardKnowledgeStepProps) {
63
+ const fileUploadAvailability = useFileUploadAvailability();
61
64
  const {
62
65
  state,
63
66
  setState,
@@ -80,18 +83,21 @@ export function NewAgentWizardKnowledgeStep(props: NewAgentWizardKnowledgeStepPr
80
83
  <button
81
84
  type="button"
82
85
  onClick={() => fileInputRef.current?.click()}
86
+ disabled={!fileUploadAvailability.isUploadAvailable}
83
87
  className={NewAgentWizardClassNames.primaryButton}
84
88
  >
85
89
  <Upload className="h-4 w-4" />
86
90
  {t('agentCreation.wizard.uploadAction')}
87
91
  </button>
88
92
  </div>
93
+ {!fileUploadAvailability.isUploadAvailable && <FileUploadUnavailableNotice className="mt-3" />}
89
94
  <input
90
95
  ref={fileInputRef}
91
96
  type="file"
92
97
  multiple
93
98
  className="hidden"
94
99
  onChange={handleKnowledgeFileSelection}
100
+ disabled={!fileUploadAvailability.isUploadAvailable}
95
101
  />
96
102
  </div>
97
103
 
@@ -9,6 +9,7 @@ import {
9
9
  type KeyboardEvent,
10
10
  type SetStateAction,
11
11
  } from 'react';
12
+ import { useFileUploadAvailability } from '../FileUploadAvailability/FileUploadAvailabilityContext';
12
13
  import { simplifyKnowledgeLabel } from '../../utils/knowledge/simplifyKnowledgeLabel';
13
14
  import { bookEditorUploadHandler } from '../../utils/upload/createBookEditorUploadHandler';
14
15
  import {
@@ -144,6 +145,7 @@ function updateKnowledgeItem(
144
145
  */
145
146
  export function useNewAgentWizardKnowledgeState(options: UseNewAgentWizardKnowledgeStateOptions) {
146
147
  const { state, setState, setStep, knowledgeStepIndex, t } = options;
148
+ const fileUploadAvailability = useFileUploadAvailability();
147
149
  const fileInputRef = useRef<HTMLInputElement | null>(null);
148
150
  const dragDepthRef = useRef(0);
149
151
  const [knowledgeFeedback, setKnowledgeFeedback] = useState<string | null>(null);
@@ -160,6 +162,11 @@ export function useNewAgentWizardKnowledgeState(options: UseNewAgentWizardKnowle
160
162
  return;
161
163
  }
162
164
 
165
+ if (!fileUploadAvailability.isUploadAvailable) {
166
+ setKnowledgeFeedback(fileUploadAvailability.message || t('agentCreation.wizard.uploadFailed'));
167
+ return;
168
+ }
169
+
163
170
  setKnowledgeFeedback(null);
164
171
 
165
172
  for (const file of files) {
@@ -297,7 +304,7 @@ export function useNewAgentWizardKnowledgeState(options: UseNewAgentWizardKnowle
297
304
  }
298
305
 
299
306
  event.preventDefault();
300
- event.dataTransfer.dropEffect = 'copy';
307
+ event.dataTransfer.dropEffect = fileUploadAvailability.isUploadAvailable ? 'copy' : 'none';
301
308
  setIsDragOverDialog(true);
302
309
  }
303
310
 
@@ -32,6 +32,13 @@ export const DEFAULT_SPAWN_AGENT_RATE_LIMIT_MAX = 5;
32
32
  */
33
33
  export const DEFAULT_SPAWN_AGENT_RATE_LIMIT_WINDOW_MS = 10 * 60 * 1000;
34
34
 
35
+ /**
36
+ * Default maximum number of failed local runner attempts before a queued message is moved to `messages/failed`.
37
+ *
38
+ * @private shared Agents Server constant
39
+ */
40
+ export const DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS = 3;
41
+
35
42
  /**
36
43
  * Stable keys used by the dedicated server-limits table.
37
44
  *
@@ -45,6 +52,7 @@ export const SERVER_LIMIT_KEYS = {
45
52
  SPAWN_AGENT_MAX_DEPTH: 'SPAWN_AGENT_MAX_DEPTH',
46
53
  SPAWN_AGENT_RATE_LIMIT_MAX: 'SPAWN_AGENT_RATE_LIMIT_MAX',
47
54
  SPAWN_AGENT_RATE_LIMIT_WINDOW_MS: 'SPAWN_AGENT_RATE_LIMIT_WINDOW_MS',
55
+ LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS: 'LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS',
48
56
  } as const;
49
57
 
50
58
  /**
@@ -68,7 +76,7 @@ export type ServerLimitUnit = 'count' | 'MB' | 'ms';
68
76
  */
69
77
  export type ServerLimitDefinition = {
70
78
  readonly key: ServerLimitKey;
71
- readonly category: 'Timeout tools' | 'Files' | 'Federation' | 'Agent spawning';
79
+ readonly category: 'Timeout tools' | 'Files' | 'Federation' | 'Agent spawning' | 'Local agent runner';
72
80
  readonly title: string;
73
81
  readonly description: string;
74
82
  readonly unit: ServerLimitUnit;
@@ -143,7 +151,8 @@ export const SERVER_LIMIT_DEFINITIONS = [
143
151
  key: SERVER_LIMIT_KEYS.SPAWN_AGENT_RATE_LIMIT_MAX,
144
152
  category: 'Agent spawning',
145
153
  title: 'Max spawned agents per window',
146
- description: 'Limits how many persistent agents one actor can create through `spawn_agent` inside one rate-limit window.',
154
+ description:
155
+ 'Limits how many persistent agents one actor can create through `spawn_agent` inside one rate-limit window.',
147
156
  unit: 'count',
148
157
  defaultValue: DEFAULT_SPAWN_AGENT_RATE_LIMIT_MAX,
149
158
  minimumValue: 1,
@@ -161,6 +170,17 @@ export const SERVER_LIMIT_DEFINITIONS = [
161
170
  step: 1_000,
162
171
  legacyMetadataKeys: [],
163
172
  },
173
+ {
174
+ key: SERVER_LIMIT_KEYS.LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
175
+ category: 'Local agent runner',
176
+ title: 'Max failed message attempts',
177
+ description: 'Stops the local coding-agent watcher from retrying the same queued chat message forever.',
178
+ unit: 'count',
179
+ defaultValue: DEFAULT_LOCAL_AGENT_RUNNER_MAX_FAILED_ATTEMPTS,
180
+ minimumValue: 1,
181
+ step: 1,
182
+ legacyMetadataKeys: [],
183
+ },
164
184
  ] satisfies ReadonlyArray<ServerLimitDefinition>;
165
185
 
166
186
  /**