@promptbook/cli 0.112.0-110 → 0.112.0-112

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 (44) hide show
  1. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +16 -0
  2. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +31 -5
  3. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +7 -5
  4. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +4 -86
  5. package/apps/agents-server/src/app/api/admin/servers/route.ts +5 -0
  6. package/apps/agents-server/src/app/api/metadata/route.ts +4 -0
  7. package/apps/agents-server/src/components/ApplicationErrorPage/ApplicationErrorPage.tsx +118 -12
  8. package/apps/agents-server/src/database/customJavascript.ts +62 -1
  9. package/apps/agents-server/src/database/customStylesheet.ts +60 -1
  10. package/apps/agents-server/src/database/getMetadata.ts +84 -3
  11. package/apps/agents-server/src/instrumentation.ts +3 -0
  12. package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +27 -62
  13. package/apps/agents-server/src/utils/errorReporting/applicationErrorHandling.ts +45 -0
  14. package/apps/agents-server/src/utils/errorReporting/refreshApplicationDocument.ts +10 -0
  15. package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +331 -0
  16. package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +8 -153
  17. package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +177 -0
  18. package/apps/agents-server/src/utils/importAgent.ts +1 -57
  19. package/apps/agents-server/src/utils/importAgentWithFallback.ts +0 -10
  20. package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +3 -1
  21. package/apps/agents-server/src/utils/serverManagement/createManagedServer/normalizeCreateServerInput.ts +6 -0
  22. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +7 -3
  23. package/apps/agents-server/src/utils/serverManagement/createManagedServer.ts +5 -0
  24. package/apps/agents-server/src/utils/userChat/listUserChats.ts +109 -0
  25. package/apps/agents-server/src/utils/userChat.ts +0 -1
  26. package/esm/index.es.js +39 -13
  27. package/esm/index.es.js.map +1 -1
  28. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
  29. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
  30. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  31. package/esm/src/version.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +23 -2
  34. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +91 -14
  35. package/src/other/templates/getTemplatesPipelineCollection.ts +801 -652
  36. package/src/version.ts +2 -2
  37. package/src/versions.txt +2 -0
  38. package/umd/index.umd.js +39 -13
  39. package/umd/index.umd.js.map +1 -1
  40. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
  41. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
  42. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  43. package/umd/src/version.d.ts +1 -1
  44. package/apps/agents-server/src/utils/userChat/getUserChatRevision.ts +0 -145
@@ -7,6 +7,11 @@ import { $provideSupabase } from './$provideSupabase';
7
7
  */
8
8
  const CUSTOM_STYLESHEET_TABLE_BASENAME = 'CustomStylesheet';
9
9
 
10
+ /**
11
+ * Process-level cache lifetime for custom stylesheet rows.
12
+ */
13
+ const CUSTOM_STYLESHEET_CACHE_TTL_MS = 30_000;
14
+
10
15
  /**
11
16
  * Stored CustomStylesheet row shape.
12
17
  *
@@ -82,6 +87,28 @@ type DynamicSupabaseClient = {
82
87
  from: (tableName: string) => DynamicCustomStylesheetTableQuery;
83
88
  };
84
89
 
90
+ /**
91
+ * Cached custom stylesheet rows keyed by the resolved table name.
92
+ *
93
+ * @private
94
+ */
95
+ const cachedCustomStylesheetsByTableName = new Map<
96
+ string,
97
+ {
98
+ readonly loadedAt: number;
99
+ readonly rowsPromise: Promise<CustomStylesheetRow[]>;
100
+ }
101
+ >();
102
+
103
+ /**
104
+ * Clears process-level stylesheet cache after admin writes.
105
+ *
106
+ * @public
107
+ */
108
+ export function invalidateCustomStylesheetCache(): void {
109
+ cachedCustomStylesheetsByTableName.clear();
110
+ }
111
+
85
112
  /**
86
113
  * Resolves the prefixed table name for CustomStylesheet.
87
114
  *
@@ -127,8 +154,37 @@ function getCustomStylesheetClient(): DynamicSupabaseClient {
127
154
  */
128
155
  export async function listCustomStylesheets(): Promise<CustomStylesheetRow[]> {
129
156
  const table = await getCustomStylesheetTableName();
130
- const supabase = getCustomStylesheetClient();
157
+ const cachedStylesheets = cachedCustomStylesheetsByTableName.get(table);
158
+ if (cachedStylesheets && Date.now() - cachedStylesheets.loadedAt < CUSTOM_STYLESHEET_CACHE_TTL_MS) {
159
+ return cachedStylesheets.rowsPromise;
160
+ }
161
+
162
+ const rowsPromise = loadCustomStylesheetsFromDatabase(table);
163
+ cachedCustomStylesheetsByTableName.set(table, {
164
+ loadedAt: Date.now(),
165
+ rowsPromise,
166
+ });
167
+
168
+ try {
169
+ return await rowsPromise;
170
+ } catch (error) {
171
+ if (cachedCustomStylesheetsByTableName.get(table)?.rowsPromise === rowsPromise) {
172
+ cachedCustomStylesheetsByTableName.delete(table);
173
+ }
174
+ throw error;
175
+ }
176
+ }
131
177
 
178
+ /**
179
+ * Reads custom stylesheet rows from the database.
180
+ *
181
+ * @param table - Resolved CustomStylesheet table name.
182
+ * @returns Stored stylesheet rows.
183
+ *
184
+ * @private
185
+ */
186
+ async function loadCustomStylesheetsFromDatabase(table: string): Promise<CustomStylesheetRow[]> {
187
+ const supabase = getCustomStylesheetClient();
132
188
  const { data, error } = await supabase.from(table).select('*').order('createdAt', { ascending: true });
133
189
 
134
190
  if (error) {
@@ -206,6 +262,7 @@ export async function saveCustomStylesheetFile({
206
262
  throw new Error(`Failed to save custom stylesheet: ${error.message || String(error)}`);
207
263
  }
208
264
 
265
+ invalidateCustomStylesheetCache();
209
266
  return data as CustomStylesheetRow;
210
267
  }
211
268
 
@@ -229,4 +286,6 @@ export async function deleteCustomStylesheetFile(id: number): Promise<void> {
229
286
 
230
287
  throw new Error(`Failed to delete custom stylesheet: ${error.message || String(error)}`);
231
288
  }
289
+
290
+ invalidateCustomStylesheetCache();
232
291
  }
@@ -10,6 +10,45 @@ import { cache } from 'react';
10
10
  */
11
11
  const metadataDefaultsMap = new Map<string, string>(metadataDefaults.map((metadata) => [metadata.key, metadata.value]));
12
12
 
13
+ /**
14
+ * Process-level cache lifetime for metadata reads.
15
+ *
16
+ * @private Internal helper for metadata lookups in `apps/agents-server`.
17
+ */
18
+ const METADATA_CACHE_TTL_MS = 30_000;
19
+
20
+ /**
21
+ * Cached metadata table payload keyed by the resolved table name.
22
+ *
23
+ * @private Internal helper for metadata lookups in `apps/agents-server`.
24
+ */
25
+ const cachedMetadataValuesByTableName = new Map<
26
+ string,
27
+ {
28
+ readonly loadedAt: number;
29
+ readonly valuesPromise: Promise<Map<string, string | null>>;
30
+ }
31
+ >();
32
+
33
+ /**
34
+ * Metadata row shape loaded by the lightweight metadata select.
35
+ *
36
+ * @private Internal helper for metadata lookups in `apps/agents-server`.
37
+ */
38
+ type MetadataValueRow = {
39
+ readonly key: string;
40
+ readonly value: string | null;
41
+ };
42
+
43
+ /**
44
+ * Clears process-level metadata cache after admin metadata writes.
45
+ *
46
+ * @public exported from `apps/agents-server`
47
+ */
48
+ export function invalidateMetadataCache(): void {
49
+ cachedMetadataValuesByTableName.clear();
50
+ }
51
+
13
52
  /**
14
53
  * Loads the full metadata table once per request so callers can cheaply project subsets.
15
54
  *
@@ -18,17 +57,59 @@ const metadataDefaultsMap = new Map<string, string>(metadataDefaults.map((metada
18
57
  * @private Internal helper for batched metadata lookups in `apps/agents-server`.
19
58
  */
20
59
  const loadAllMetadataValues = cache(async (): Promise<Map<string, string | null>> => {
21
- const supabase = $provideSupabase();
22
60
  const table = await $getTableName('Metadata');
61
+ return loadCachedMetadataValues(table);
62
+ });
63
+
64
+ /**
65
+ * Loads metadata values using a short process-level cache shared by consecutive requests.
66
+ *
67
+ * @param table - Resolved metadata table name.
68
+ * @returns Metadata values keyed by metadata key.
69
+ *
70
+ * @private Internal helper for metadata lookups in `apps/agents-server`.
71
+ */
72
+ async function loadCachedMetadataValues(table: string): Promise<Map<string, string | null>> {
73
+ const cachedMetadataValues = cachedMetadataValuesByTableName.get(table);
74
+ if (cachedMetadataValues && Date.now() - cachedMetadataValues.loadedAt < METADATA_CACHE_TTL_MS) {
75
+ return cachedMetadataValues.valuesPromise;
76
+ }
77
+
78
+ const valuesPromise = loadMetadataValuesFromDatabase(table);
79
+ cachedMetadataValuesByTableName.set(table, {
80
+ loadedAt: Date.now(),
81
+ valuesPromise,
82
+ });
83
+
84
+ try {
85
+ return await valuesPromise;
86
+ } catch (error) {
87
+ if (cachedMetadataValuesByTableName.get(table)?.valuesPromise === valuesPromise) {
88
+ cachedMetadataValuesByTableName.delete(table);
89
+ }
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Reads all persisted metadata values from the database.
96
+ *
97
+ * @param table - Resolved metadata table name.
98
+ * @returns Metadata values keyed by metadata key.
99
+ *
100
+ * @private Internal helper for metadata lookups in `apps/agents-server`.
101
+ */
102
+ async function loadMetadataValuesFromDatabase(table: string): Promise<Map<string, string | null>> {
103
+ const supabase = $provideSupabase();
23
104
  const { data } = await supabase.from(table).select('key, value');
24
105
 
25
106
  const loadedMap = new Map<string, string | null>();
26
- for (const row of data ?? []) {
107
+ for (const row of (data ?? []) as Array<MetadataValueRow>) {
27
108
  loadedMap.set(row.key, row.value);
28
109
  }
29
110
 
30
111
  return loadedMap;
31
- });
112
+ }
32
113
 
33
114
  /**
34
115
  * Get metadata value by key
@@ -10,6 +10,9 @@ export async function register(): Promise<void> {
10
10
  }
11
11
 
12
12
  try {
13
+ const { registerServerErrorSentryLogging } = await import('./utils/errorReporting/registerServerErrorSentryLogging');
14
+ registerServerErrorSentryLogging();
15
+
13
16
  const { registerNodeRuntimeInstrumentation } = await import('./instrumentation-node');
14
17
  await registerNodeRuntimeInstrumentation();
15
18
  } catch (error) {
@@ -1,15 +1,11 @@
1
1
  import { $provideServer } from '../../tools/$provideServer';
2
2
  import { $provideAgentCollectionForServer } from '../../tools/$provideAgentCollectionForServer';
3
- import { $getTableName } from '../../database/$getTableName';
4
- import { $provideSupabaseForServer } from '../../database/$provideSupabaseForServer';
5
3
  import { $provideAgentReferenceResolver } from '../agentReferenceResolver/$provideAgentReferenceResolver';
6
4
  import { consumeAgentReferenceResolutionIssues } from '../agentReferenceResolver/AgentReferenceResolutionIssue';
7
5
  import { parseBookScopedAgentIdentifier } from '../agentReferenceResolver/bookScopedAgentReferences';
8
- import { buildAgentNameOrIdFilter } from '../agentIdentifier';
9
6
  import { resolvePseudoAgentDescriptor } from '../pseudoAgents';
10
7
  import { normalizeAgentName } from '../../../../../src/_packages/core.index';
11
8
  import type { PseudoAgentKind } from '../../../../../src/book-2.0/agent-source/pseudoAgentReferences';
12
- import type { AgentsServerDatabase } from '../../database/schema';
13
9
  import { cache } from 'react';
14
10
 
15
11
  /**
@@ -54,14 +50,6 @@ type PseudoAgentRouteTarget = {
54
50
  canonicalUrl: string;
55
51
  };
56
52
 
57
- /**
58
- * Minimal database row needed to resolve one local route target.
59
- */
60
- type LocalAgentRouteRow = Pick<
61
- AgentsServerDatabase['public']['Tables']['Agent']['Row'],
62
- 'agentName' | 'permanentId'
63
- >;
64
-
65
53
  /**
66
54
  * Target returned for an incoming `/agents/:agentId` route value.
67
55
  */
@@ -114,11 +102,6 @@ async function resolveAgentRouteTargetUncached(
114
102
  };
115
103
  }
116
104
 
117
- const directLocalRouteTarget = await resolveLocalAgentRouteTargetFromDatabase(normalizedReference, localServerUrl);
118
- if (directLocalRouteTarget) {
119
- return directLocalRouteTarget;
120
- }
121
-
122
105
  const resolver = await $provideAgentReferenceResolver({ forceRefresh: options?.forceRefresh });
123
106
  let resolvedUrlValue: string;
124
107
 
@@ -288,21 +271,7 @@ async function resolveLocalAgentRouteTarget(
288
271
  localServerUrl: string,
289
272
  ): Promise<AgentRouteTarget | null> {
290
273
  const collection = await $provideAgentCollectionForServer();
291
- const agents = await collection.listAgents();
292
- const normalizedReference = normalizeAgentName(reference);
293
-
294
- const agentMatch = agents.find((agent: { agentName: string; permanentId?: string }) => {
295
- if (agent.agentName === reference || agent.permanentId === reference) {
296
- return true;
297
- }
298
-
299
- const normalizedAgentName = normalizeAgentName(agent.agentName);
300
- if (normalizedAgentName === normalizedReference) {
301
- return true;
302
- }
303
-
304
- return false;
305
- });
274
+ const agentMatch = await findLocalAgentRouteMatch(collection, reference);
306
275
 
307
276
  if (!agentMatch) {
308
277
  return null;
@@ -318,40 +287,36 @@ async function resolveLocalAgentRouteTarget(
318
287
  }
319
288
 
320
289
  /**
321
- * Resolves the common local route case with a targeted indexed database lookup.
290
+ * Finds a local agent route match without loading the whole collection when possible.
322
291
  *
323
- * The reference resolver has to initialize the full local/federated lookup map.
324
- * Most page requests already carry a permanent id or exact local name, so checking
325
- * the Agent table first keeps normal navigation from loading every agent profile.
326
- *
327
- * @param reference - Normalized route reference.
328
- * @param localServerUrl - Normalized URL of the current Agents Server instance.
329
- * @returns Local route target or `null` when the reference is not a local agent.
292
+ * @param collection - Agents collection backing local routes.
293
+ * @param reference - Normalized route/reference text.
294
+ * @returns Matching local agent profile or `null`.
330
295
  */
331
- async function resolveLocalAgentRouteTargetFromDatabase(
296
+ async function findLocalAgentRouteMatch(
297
+ collection: Awaited<ReturnType<typeof $provideAgentCollectionForServer>>,
332
298
  reference: string,
333
- localServerUrl: string,
334
- ): Promise<AgentRouteTarget | null> {
335
- const supabase = $provideSupabaseForServer();
336
- const agentTable = await $getTableName('Agent');
337
- const result = await supabase
338
- .from(agentTable)
339
- .select('agentName, permanentId')
340
- .or(buildAgentNameOrIdFilter(reference))
341
- .is('deletedAt', null)
342
- .order('createdAt', { ascending: true })
343
- .limit(1);
344
-
345
- if (result.error || !result.data || result.data.length === 0) {
346
- return null;
299
+ ): Promise<{ agentName: string; permanentId?: string | null } | null> {
300
+ const directMatch = await collection.findAgentBasicInformation(reference);
301
+ if (directMatch) {
302
+ return directMatch;
347
303
  }
348
304
 
349
- const agent = result.data[0] as LocalAgentRouteRow;
350
- const canonicalAgentId = agent.permanentId || agent.agentName;
305
+ const agents = await collection.listAgents();
306
+ const normalizedReference = normalizeAgentName(reference);
351
307
 
352
- return {
353
- kind: 'local',
354
- canonicalAgentId,
355
- canonicalUrl: `${localServerUrl}${AGENT_PATH_PREFIX}${encodeURIComponent(canonicalAgentId)}`,
356
- };
308
+ return (
309
+ agents.find((agent: { agentName: string; permanentId?: string | null }) => {
310
+ if (agent.agentName === reference || agent.permanentId === reference) {
311
+ return true;
312
+ }
313
+
314
+ const normalizedAgentName = normalizeAgentName(agent.agentName);
315
+ if (normalizedAgentName === normalizedReference) {
316
+ return true;
317
+ }
318
+
319
+ return false;
320
+ }) || null
321
+ );
357
322
  }
@@ -80,6 +80,21 @@ const EDGE_DASH_PATTERN = /^-+|-+$/g;
80
80
  */
81
81
  const CODE_FENCE_PATTERN = /```/g;
82
82
 
83
+ /**
84
+ * Regex matching webpack/Next.js chunk-load failures caused by stale or missing build assets.
85
+ */
86
+ const CHUNK_LOAD_ERROR_MESSAGE_PATTERN = /loading (css )?chunk .* failed/i;
87
+
88
+ /**
89
+ * Regex matching browsers that fail while fetching one dynamically imported module.
90
+ */
91
+ const DYNAMIC_IMPORT_FETCH_ERROR_MESSAGE_PATTERN = /failed to fetch dynamically imported module/i;
92
+
93
+ /**
94
+ * Regex matching Next.js-managed build asset URLs.
95
+ */
96
+ const NEXT_BUILD_ASSET_PATH_PATTERN = /\/_next\/static\/(?:chunks|css)\//i;
97
+
83
98
  /**
84
99
  * Shape sent from the app error boundary to server-side Sentry forwarding.
85
100
  */
@@ -162,6 +177,32 @@ export function createApplicationErrorDigest(error: ApplicationBoundaryError | n
162
177
  return (hash >>> 0).toString(DECIMAL_BASE).padStart(DIGEST_LENGTH, '0');
163
178
  }
164
179
 
180
+ /**
181
+ * Detects client-side build-asset errors that are commonly fixed by reloading the document once.
182
+ *
183
+ * @param error - Captured boundary exception.
184
+ * @returns True when the current document should be hard-refreshed instead of only retrying React navigation.
185
+ */
186
+ export function isApplicationErrorRecoverableByHardRefresh(error: ApplicationBoundaryError | null): boolean {
187
+ const errorName = error?.name?.trim() || '';
188
+ const errorMessage = error?.message?.trim() || '';
189
+ const errorStack = error?.stack?.trim() || '';
190
+ const errorDetails = `${errorName}\n${errorMessage}\n${errorStack}`;
191
+
192
+ if (errorName === 'ChunkLoadError') {
193
+ return true;
194
+ }
195
+
196
+ if (CHUNK_LOAD_ERROR_MESSAGE_PATTERN.test(errorDetails)) {
197
+ return true;
198
+ }
199
+
200
+ return (
201
+ DYNAMIC_IMPORT_FETCH_ERROR_MESSAGE_PATTERN.test(errorDetails) &&
202
+ NEXT_BUILD_ASSET_PATH_PATTERN.test(errorDetails)
203
+ );
204
+ }
205
+
165
206
  /**
166
207
  * Formats descriptive text shown beneath the application error headline.
167
208
  *
@@ -170,6 +211,10 @@ export function createApplicationErrorDigest(error: ApplicationBoundaryError | n
170
211
  * @returns Friendly summary of what happened.
171
212
  */
172
213
  export function describeApplicationError(error: ApplicationBoundaryError | null, serverName: string): string {
214
+ if (isApplicationErrorRecoverableByHardRefresh(error)) {
215
+ return `The browser could not load the latest application files for ${serverName}. Refreshing the page usually resolves this after a deployment or stale cached shell.`;
216
+ }
217
+
173
218
  if (error?.message) {
174
219
  return `${error.message.trim()} - the server for ${serverName} logged this failure.`;
175
220
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reloads the current browser document so stale build assets can be fetched again.
3
+ */
4
+ export function refreshApplicationDocument(): void {
5
+ if (typeof window === 'undefined') {
6
+ return;
7
+ }
8
+
9
+ window.location.reload();
10
+ }