@promptbook/core 0.100.0-3 → 0.100.0-32

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 (59) hide show
  1. package/README.md +1 -0
  2. package/esm/index.es.js +2348 -351
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/src/_packages/components.index.d.ts +4 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +24 -0
  6. package/esm/typings/src/_packages/types.index.d.ts +30 -0
  7. package/esm/typings/src/book-2.0/agent-source/parseAgentSource.d.ts +30 -0
  8. package/esm/typings/src/book-2.0/agent-source/parseAgentSource.test.d.ts +1 -0
  9. package/esm/typings/src/book-2.0/agent-source/string_book.d.ts +26 -0
  10. package/esm/typings/src/book-2.0/commitments/ACTION/ACTION.d.ts +38 -0
  11. package/esm/typings/src/book-2.0/commitments/FORMAT/FORMAT.d.ts +39 -0
  12. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/FrontendRAGService.d.ts +48 -0
  13. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +51 -0
  14. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/RAGService.d.ts +54 -0
  15. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/BaseKnowledgeProcessor.d.ts +45 -0
  16. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/PdfProcessor.d.ts +31 -0
  17. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/ProcessorFactory.d.ts +23 -0
  18. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/TextProcessor.d.ts +18 -0
  19. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/types.d.ts +56 -0
  20. package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/utils/ragHelper.d.ts +34 -0
  21. package/esm/typings/src/book-2.0/commitments/META_IMAGE/META_IMAGE.d.ts +44 -0
  22. package/esm/typings/src/book-2.0/commitments/META_LINK/META_LINK.d.ts +56 -0
  23. package/esm/typings/src/book-2.0/commitments/MODEL/MODEL.d.ts +39 -0
  24. package/esm/typings/src/book-2.0/commitments/NOTE/NOTE.d.ts +49 -0
  25. package/esm/typings/src/book-2.0/commitments/PERSONA/PERSONA.d.ts +46 -0
  26. package/esm/typings/src/book-2.0/commitments/RULE/RULE.d.ts +44 -0
  27. package/esm/typings/src/book-2.0/commitments/SAMPLE/SAMPLE.d.ts +44 -0
  28. package/esm/typings/src/book-2.0/commitments/STYLE/STYLE.d.ts +38 -0
  29. package/esm/typings/src/book-2.0/commitments/_base/BaseCommitmentDefinition.d.ts +52 -0
  30. package/esm/typings/src/book-2.0/commitments/_base/BookCommitment.d.ts +5 -0
  31. package/esm/typings/src/book-2.0/commitments/_base/CommitmentDefinition.d.ts +48 -0
  32. package/esm/typings/src/book-2.0/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +22 -0
  33. package/esm/typings/src/book-2.0/commitments/_base/createEmptyAgentModelRequirements.d.ts +19 -0
  34. package/esm/typings/src/book-2.0/commitments/_misc/AgentModelRequirements.d.ts +37 -0
  35. package/esm/typings/src/book-2.0/commitments/_misc/AgentSourceParseResult.d.ts +18 -0
  36. package/esm/typings/src/book-2.0/commitments/_misc/ParsedCommitment.d.ts +22 -0
  37. package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirements.d.ts +61 -0
  38. package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirementsWithCommitments.d.ts +35 -0
  39. package/esm/typings/src/book-2.0/commitments/_misc/createCommitmentRegex.d.ts +20 -0
  40. package/esm/typings/src/book-2.0/commitments/_misc/parseAgentSourceWithCommitments.d.ts +24 -0
  41. package/esm/typings/src/book-2.0/commitments/_misc/removeCommentsFromSystemMessage.d.ts +11 -0
  42. package/esm/typings/src/book-2.0/commitments/index.d.ts +56 -0
  43. package/esm/typings/src/book-2.0/utils/profileImageUtils.d.ts +39 -0
  44. package/esm/typings/src/book-components/BookEditor/BookEditor.d.ts +26 -0
  45. package/esm/typings/src/execution/AvailableModel.d.ts +4 -0
  46. package/esm/typings/src/execution/createPipelineExecutor/40-executeAttempts.d.ts +6 -1
  47. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +0 -5
  48. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  49. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +1 -1
  50. package/esm/typings/src/llm-providers/google/google-models.d.ts +1 -1
  51. package/esm/typings/src/llm-providers/ollama/ollama-models.d.ts +1 -1
  52. package/esm/typings/src/llm-providers/openai/openai-models.d.ts +1 -1
  53. package/esm/typings/src/pipeline/book-notation.d.ts +2 -1
  54. package/esm/typings/src/types/ModelRequirements.d.ts +0 -2
  55. package/esm/typings/src/types/typeAliases.d.ts +6 -0
  56. package/esm/typings/src/version.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/umd/index.umd.js +2213 -204
  59. package/umd/index.umd.js.map +1 -1
package/umd/index.umd.js CHANGED
@@ -27,136 +27,342 @@
27
27
  * @generated
28
28
  * @see https://github.com/webgptorg/promptbook
29
29
  */
30
- const PROMPTBOOK_ENGINE_VERSION = '0.100.0-3';
30
+ const PROMPTBOOK_ENGINE_VERSION = '0.100.0-32';
31
31
  /**
32
32
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
33
33
  * Note: [💞] Ignore a discrepancy between file name and entity name
34
34
  */
35
35
 
36
36
  /**
37
- * Converts PipelineCollection to serialized JSON
37
+ * Extracts profile image URL from agent definition text and returns cleaned system message
38
+ * @param systemMessage The original system message that may contain META IMAGE line
39
+ * @returns Object with profileImageUrl (if found) and cleanedSystemMessage (without META IMAGE line)
38
40
  *
39
- * Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
41
+ * @private - TODO: [🧠] Maybe should be public?
42
+ */
43
+ /**
44
+ * Generates a gravatar URL based on agent name for fallback avatar
45
+ * @param name The agent name to generate avatar for
46
+ * @returns Gravatar URL
40
47
  *
41
- * @public exported from `@promptbook/core`
48
+ * @private - TODO: [🧠] Maybe should be public?
42
49
  */
43
- async function collectionToJson(collection) {
44
- const pipelineUrls = await collection.listPipelines();
45
- const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
46
- return promptbooks;
50
+ function generateGravatarUrl(name) {
51
+ // Use a default name if none provided
52
+ const safeName = name || 'Anonymous Agent';
53
+ // Create a simple hash from the name for consistent avatar
54
+ let hash = 0;
55
+ for (let i = 0; i < safeName.length; i++) {
56
+ const char = safeName.charCodeAt(i);
57
+ hash = (hash << 5) - hash + char;
58
+ hash = hash & hash; // Convert to 32bit integer
59
+ }
60
+ const avatarId = Math.abs(hash).toString();
61
+ return `https://www.gravatar.com/avatar/${avatarId}?default=robohash&size=200&rating=x`;
47
62
  }
48
63
  /**
49
- * TODO: [🧠] Maybe clear `sourceFile` or clear when exposing through API or remote server
64
+ * Note: [💞] Ignore a discrepancy between file name and entity name
50
65
  */
51
66
 
52
67
  /**
53
- * Checks if value is valid email
68
+ * Freezes the given object and all its nested objects recursively
69
+ *
70
+ * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
71
+ * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
54
72
  *
73
+ * @returns The same object as the input, but deeply frozen
55
74
  * @public exported from `@promptbook/utils`
56
75
  */
57
- function isValidEmail(email) {
58
- if (typeof email !== 'string') {
59
- return false;
76
+ function $deepFreeze(objectValue) {
77
+ if (Array.isArray(objectValue)) {
78
+ return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
60
79
  }
61
- if (email.split('\n').length > 1) {
62
- return false;
80
+ const propertyNames = Object.getOwnPropertyNames(objectValue);
81
+ for (const propertyName of propertyNames) {
82
+ const value = objectValue[propertyName];
83
+ if (value && typeof value === 'object') {
84
+ $deepFreeze(value);
85
+ }
63
86
  }
64
- return /^.+@.+\..+$/.test(email);
87
+ Object.freeze(objectValue);
88
+ return objectValue;
65
89
  }
90
+ /**
91
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
92
+ */
66
93
 
67
94
  /**
68
- * Tests if given string is valid URL.
95
+ * Generates a regex pattern to match a specific commitment
69
96
  *
70
- * Note: This does not check if the file exists only if the path is valid
71
- * @public exported from `@promptbook/utils`
97
+ * Note: It always creates new Regex object
98
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
99
+ *
100
+ * @private
72
101
  */
73
- function isValidFilePath(filename) {
74
- if (typeof filename !== 'string') {
75
- return false;
102
+ function createCommitmentRegex(commitment) {
103
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
104
+ const keywordPattern = escapedCommitment.split(/\s+/).join('\\s+');
105
+ const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
106
+ return regex;
107
+ }
108
+ /**
109
+ * Generates a regex pattern to match a specific commitment type
110
+ *
111
+ * Note: It just matches the type part of the commitment
112
+ * Note: It always creates new Regex object
113
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
114
+ *
115
+ * @private
116
+ */
117
+ function createCommitmentTypeRegex(commitment) {
118
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
119
+ const keywordPattern = escapedCommitment.split(/\s+/).join('\\s+');
120
+ const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
121
+ return regex;
122
+ }
123
+
124
+ /**
125
+ * Base implementation of CommitmentDefinition that provides common functionality
126
+ * Most commitments can extend this class and only override the applyToAgentModelRequirements method
127
+ *
128
+ * @private
129
+ */
130
+ class BaseCommitmentDefinition {
131
+ constructor(type) {
132
+ this.type = type;
76
133
  }
77
- if (filename.split('\n').length > 1) {
78
- return false;
134
+ /**
135
+ * Creates a regex pattern to match this commitment in agent source
136
+ * Uses the existing createCommitmentRegex function as internal helper
137
+ */
138
+ createRegex() {
139
+ return createCommitmentRegex(this.type);
79
140
  }
80
- if (filename.split(' ').length >
81
- 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
82
- return false;
141
+ /**
142
+ * Creates a regex pattern to match just the commitment type
143
+ * Uses the existing createCommitmentTypeRegex function as internal helper
144
+ */
145
+ createTypeRegex() {
146
+ return createCommitmentTypeRegex(this.type);
83
147
  }
84
- const filenameSlashes = filename.split('\\').join('/');
85
- // Absolute Unix path: /hello.txt
86
- if (/^(\/)/i.test(filenameSlashes)) {
87
- // console.log(filename, 'Absolute Unix path: /hello.txt');
88
- return true;
148
+ /**
149
+ * Helper method to create a new requirements object with updated system message
150
+ * This is commonly used by many commitments
151
+ */
152
+ updateSystemMessage(requirements, messageUpdate) {
153
+ const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
154
+ return {
155
+ ...requirements,
156
+ systemMessage: newMessage,
157
+ };
89
158
  }
90
- // Absolute Windows path: /hello.txt
91
- if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
92
- // console.log(filename, 'Absolute Windows path: /hello.txt');
93
- return true;
159
+ /**
160
+ * Helper method to append content to the system message
161
+ */
162
+ appendToSystemMessage(requirements, content, separator = '\n\n') {
163
+ return this.updateSystemMessage(requirements, (currentMessage) => {
164
+ if (!currentMessage.trim()) {
165
+ return content;
166
+ }
167
+ return currentMessage + separator + content;
168
+ });
94
169
  }
95
- // Relative path: ./hello.txt
96
- if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
97
- // console.log(filename, 'Relative path: ./hello.txt');
98
- return true;
170
+ /**
171
+ * Helper method to add a comment section to the system message
172
+ * Comments are lines starting with # that will be removed from the final system message
173
+ * but can be useful for organizing and structuring the message during processing
174
+ */
175
+ addCommentSection(requirements, commentTitle, content, position = 'end') {
176
+ const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
177
+ if (position === 'beginning') {
178
+ return this.updateSystemMessage(requirements, (currentMessage) => {
179
+ if (!currentMessage.trim()) {
180
+ return commentSection;
181
+ }
182
+ return commentSection + '\n\n' + currentMessage;
183
+ });
184
+ }
185
+ else {
186
+ return this.appendToSystemMessage(requirements, commentSection);
187
+ }
99
188
  }
100
- // Allow paths like foo/hello
101
- if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
102
- // console.log(filename, 'Allow paths like foo/hello');
103
- return true;
189
+ }
190
+
191
+ /**
192
+ * ACTION commitment definition
193
+ *
194
+ * The ACTION commitment defines specific actions or capabilities that the agent can perform.
195
+ * This helps define what the agent is capable of doing and how it should approach tasks.
196
+ *
197
+ * Example usage in agent source:
198
+ *
199
+ * ```book
200
+ * ACTION Can generate code snippets and explain programming concepts
201
+ * ACTION Able to analyze data and provide insights
202
+ * ```
203
+ *
204
+ * @private [🪔] Maybe export the commitments through some package
205
+ */
206
+ class ActionCommitmentDefinition extends BaseCommitmentDefinition {
207
+ constructor() {
208
+ super('ACTION');
104
209
  }
105
- // Allow paths like hello.book
106
- if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
107
- // console.log(filename, 'Allow paths like hello.book');
108
- return true;
210
+ /**
211
+ * Short one-line description of ACTION.
212
+ */
213
+ get description() {
214
+ return 'Define agent capabilities and actions it can perform.';
215
+ }
216
+ /**
217
+ * Markdown documentation for ACTION commitment.
218
+ */
219
+ get documentation() {
220
+ return spaceTrim.spaceTrim(`
221
+ # ACTION
222
+
223
+ Defines specific actions or capabilities that the agent can perform.
224
+
225
+ ## Key behaviors
226
+
227
+ - Multiple \`ACTION\` commitments are applied sequentially.
228
+ - Each action adds to the agent's capability list.
229
+ - Actions help users understand what the agent can do.
230
+
231
+ ## Examples
232
+
233
+ \`\`\`book
234
+ Code Assistant
235
+
236
+ PERSONA You are a programming assistant
237
+ ACTION Can generate code snippets and explain programming concepts
238
+ ACTION Able to debug existing code and suggest improvements
239
+ ACTION Can create unit tests for functions
240
+ \`\`\`
241
+
242
+ \`\`\`book
243
+ Data Scientist
244
+
245
+ PERSONA You are a data analysis expert
246
+ ACTION Able to analyze data and provide insights
247
+ ACTION Can create visualizations and charts
248
+ ACTION Capable of statistical analysis and modeling
249
+ KNOWLEDGE Data analysis best practices and statistical methods
250
+ \`\`\`
251
+ `);
252
+ }
253
+ applyToAgentModelRequirements(requirements, content) {
254
+ const trimmedContent = content.trim();
255
+ if (!trimmedContent) {
256
+ return requirements;
257
+ }
258
+ // Add action capability to the system message
259
+ const actionSection = `Capability: ${trimmedContent}`;
260
+ return this.appendToSystemMessage(requirements, actionSection, '\n\n');
109
261
  }
110
- return false;
111
262
  }
112
263
  /**
113
- * TODO: [🍏] Implement for MacOs
264
+ * Singleton instance of the ACTION commitment definition
265
+ *
266
+ * @private [🪔] Maybe export the commitments through some package
267
+ */
268
+ new ActionCommitmentDefinition();
269
+ /**
270
+ * Note: [💞] Ignore a discrepancy between file name and entity name
114
271
  */
115
272
 
116
273
  /**
117
- * Tests if given string is valid URL.
274
+ * FORMAT commitment definition
118
275
  *
119
- * Note: Dataurl are considered perfectly valid.
120
- * Note: There are two similar functions:
121
- * - `isValidUrl` which tests any URL
122
- * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
276
+ * The FORMAT commitment defines the specific output structure and formatting
277
+ * that the agent should use in its responses. This includes data formats,
278
+ * response templates, and structural requirements.
123
279
  *
124
- * @public exported from `@promptbook/utils`
280
+ * Example usage in agent source:
281
+ *
282
+ * ```book
283
+ * FORMAT Always respond in JSON format with 'status' and 'data' fields
284
+ * FORMAT Use markdown formatting for all code blocks
285
+ * ```
286
+ *
287
+ * @private [🪔] Maybe export the commitments through some package
125
288
  */
126
- function isValidUrl(url) {
127
- if (typeof url !== 'string') {
128
- return false;
289
+ class FormatCommitmentDefinition extends BaseCommitmentDefinition {
290
+ constructor() {
291
+ super('FORMAT');
129
292
  }
130
- try {
131
- if (url.startsWith('blob:')) {
132
- url = url.replace(/^blob:/, '');
133
- }
134
- const urlObject = new URL(url /* because fail is handled */);
135
- if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
136
- return false;
137
- }
138
- return true;
293
+ /**
294
+ * Short one-line description of FORMAT.
295
+ */
296
+ get description() {
297
+ return 'Specify output structure or formatting requirements.';
139
298
  }
140
- catch (error) {
141
- return false;
299
+ /**
300
+ * Markdown documentation for FORMAT commitment.
301
+ */
302
+ get documentation() {
303
+ return spaceTrim.spaceTrim(`
304
+ # FORMAT
305
+
306
+ Defines the specific output structure and formatting for responses (data formats, templates, structure).
307
+
308
+ ## Key behaviors
309
+
310
+ - Multiple \`FORMAT\` commitments are applied sequentially.
311
+ - If they are in conflict, the last one takes precedence.
312
+ - You can specify both data formats and presentation styles.
313
+
314
+ ## Examples
315
+
316
+ \`\`\`book
317
+ Customer Support Bot
318
+
319
+ PERSONA You are a helpful customer support agent
320
+ FORMAT Always respond in JSON format with 'status' and 'data' fields
321
+ FORMAT Use markdown formatting for all code blocks
322
+ \`\`\`
323
+
324
+ \`\`\`book
325
+ Data Analyst
326
+
327
+ PERSONA You are a data analysis expert
328
+ FORMAT Present results in structured tables
329
+ FORMAT Include confidence scores for all predictions
330
+ STYLE Be concise and precise in explanations
331
+ \`\`\`
332
+ `);
333
+ }
334
+ applyToAgentModelRequirements(requirements, content) {
335
+ const trimmedContent = content.trim();
336
+ if (!trimmedContent) {
337
+ return requirements;
338
+ }
339
+ // Add format instructions to the system message
340
+ const formatSection = `Output Format: ${trimmedContent}`;
341
+ return this.appendToSystemMessage(requirements, formatSection, '\n\n');
142
342
  }
143
343
  }
344
+ /**
345
+ * Singleton instance of the FORMAT commitment definition
346
+ *
347
+ * @private [🪔] Maybe export the commitments through some package
348
+ */
349
+ new FormatCommitmentDefinition();
350
+ /**
351
+ * Note: [💞] Ignore a discrepancy between file name and entity name
352
+ */
144
353
 
145
354
  /**
146
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
355
+ * Error thrown when a fetch request fails
147
356
  *
148
357
  * @public exported from `@promptbook/core`
149
358
  */
150
- class ParseError extends Error {
359
+ class PromptbookFetchError extends Error {
151
360
  constructor(message) {
152
361
  super(message);
153
- this.name = 'ParseError';
154
- Object.setPrototypeOf(this, ParseError.prototype);
362
+ this.name = 'PromptbookFetchError';
363
+ Object.setPrototypeOf(this, PromptbookFetchError.prototype);
155
364
  }
156
365
  }
157
- /**
158
- * TODO: Maybe split `ParseError` and `ApplyError`
159
- */
160
366
 
161
367
  /**
162
368
  * Available remote servers for the Promptbook
@@ -553,76 +759,1907 @@
553
759
  }
554
760
 
555
761
  /**
556
- * This error type indicates that the error should not happen and its last check before crashing with some other error
762
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
763
+ *
764
+ * @public exported from `@promptbook/core`
765
+ */
766
+ class UnexpectedError extends Error {
767
+ constructor(message) {
768
+ super(spaceTrim.spaceTrim((block) => `
769
+ ${block(message)}
770
+
771
+ Note: This error should not happen.
772
+ It's probably a bug in the pipeline collection
773
+
774
+ Please report issue:
775
+ ${block(getErrorReportUrl(new Error(message)).href)}
776
+
777
+ Or contact us on ${ADMIN_EMAIL}
778
+
779
+ `));
780
+ this.name = 'UnexpectedError';
781
+ Object.setPrototypeOf(this, UnexpectedError.prototype);
782
+ }
783
+ }
784
+
785
+ /**
786
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
787
+ *
788
+ * @public exported from `@promptbook/core`
789
+ */
790
+ class WrappedError extends Error {
791
+ constructor(whatWasThrown) {
792
+ const tag = `[🤮]`;
793
+ console.error(tag, whatWasThrown);
794
+ super(spaceTrim.spaceTrim(`
795
+ Non-Error object was thrown
796
+
797
+ Note: Look for ${tag} in the console for more details
798
+ Please report issue on ${ADMIN_EMAIL}
799
+ `));
800
+ this.name = 'WrappedError';
801
+ Object.setPrototypeOf(this, WrappedError.prototype);
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
807
+ *
808
+ * @param whatWasThrown Any object that was thrown
809
+ * @returns Nothing if the error is an instance of `Error`
810
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
811
+ *
812
+ * @private within the repository
813
+ */
814
+ function assertsError(whatWasThrown) {
815
+ // Case 1: Handle error which was rethrown as `WrappedError`
816
+ if (whatWasThrown instanceof WrappedError) {
817
+ const wrappedError = whatWasThrown;
818
+ throw wrappedError;
819
+ }
820
+ // Case 2: Handle unexpected errors
821
+ if (whatWasThrown instanceof UnexpectedError) {
822
+ const unexpectedError = whatWasThrown;
823
+ throw unexpectedError;
824
+ }
825
+ // Case 3: Handle standard errors - keep them up to consumer
826
+ if (whatWasThrown instanceof Error) {
827
+ return;
828
+ }
829
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
830
+ throw new WrappedError(whatWasThrown);
831
+ }
832
+
833
+ /**
834
+ * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
835
+ *
836
+ * @public exported from `@promptbook/core`
837
+ */
838
+ const promptbookFetch = async (urlOrRequest, init) => {
839
+ try {
840
+ return await fetch(urlOrRequest, init);
841
+ }
842
+ catch (error) {
843
+ assertsError(error);
844
+ let url;
845
+ if (typeof urlOrRequest === 'string') {
846
+ url = urlOrRequest;
847
+ }
848
+ else if (urlOrRequest instanceof Request) {
849
+ url = urlOrRequest.url;
850
+ }
851
+ throw new PromptbookFetchError(spaceTrim__default["default"]((block) => `
852
+ Can not fetch "${url}"
853
+
854
+ Fetch error:
855
+ ${block(error.message)}
856
+
857
+ `));
858
+ }
859
+ };
860
+ /**
861
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
862
+ */
863
+
864
+ /**
865
+ * Frontend RAG Service that uses backend APIs for processing
866
+ * This avoids Node.js dependencies in the frontend
867
+ *
868
+ * @private - TODO: [🧠] Maybe should be public?
869
+ */
870
+ class FrontendRAGService {
871
+ constructor(config) {
872
+ this.chunks = [];
873
+ this.sources = [];
874
+ this.isInitialized = false;
875
+ this.config = {
876
+ maxChunkSize: 1000,
877
+ chunkOverlap: 200,
878
+ maxRetrievedChunks: 5,
879
+ minRelevanceScore: 0.1,
880
+ ...config,
881
+ };
882
+ }
883
+ /**
884
+ * Initialize knowledge sources by processing them on the backend
885
+ */
886
+ async initializeKnowledgeSources(sources) {
887
+ if (sources.length === 0) {
888
+ this.isInitialized = true;
889
+ return;
890
+ }
891
+ try {
892
+ const response = await promptbookFetch('/api/knowledge/process-sources', {
893
+ method: 'POST',
894
+ headers: {
895
+ 'Content-Type': 'application/json',
896
+ },
897
+ body: JSON.stringify({
898
+ sources,
899
+ config: {
900
+ maxChunkSize: this.config.maxChunkSize,
901
+ chunkOverlap: this.config.chunkOverlap,
902
+ },
903
+ }),
904
+ });
905
+ if (!response.ok) {
906
+ throw new Error(`Failed to process knowledge sources: ${response.status}`);
907
+ }
908
+ const result = (await response.json());
909
+ if (!result.success) {
910
+ throw new Error(result.message || 'Failed to process knowledge sources');
911
+ }
912
+ this.chunks = result.chunks;
913
+ this.sources = sources;
914
+ this.isInitialized = true;
915
+ console.log(`Initialized RAG service with ${this.chunks.length} chunks from ${sources.length} sources`);
916
+ }
917
+ catch (error) {
918
+ console.error('Failed to initialize knowledge sources:', error);
919
+ // Don't throw - allow the system to continue without RAG
920
+ this.isInitialized = true;
921
+ }
922
+ }
923
+ /**
924
+ * Get relevant context for a user query
925
+ */
926
+ async getContextForQuery(query) {
927
+ if (!this.isInitialized) {
928
+ console.warn('RAG service not initialized');
929
+ return '';
930
+ }
931
+ if (this.chunks.length === 0) {
932
+ return '';
933
+ }
934
+ try {
935
+ const response = await promptbookFetch('/api/knowledge/retrieve-context', {
936
+ method: 'POST',
937
+ headers: {
938
+ 'Content-Type': 'application/json',
939
+ },
940
+ body: JSON.stringify({
941
+ query,
942
+ chunks: this.chunks,
943
+ config: {
944
+ maxRetrievedChunks: this.config.maxRetrievedChunks,
945
+ minRelevanceScore: this.config.minRelevanceScore,
946
+ },
947
+ }),
948
+ });
949
+ if (!response.ok) {
950
+ console.error(`Failed to retrieve context: ${response.status}`);
951
+ return '';
952
+ }
953
+ const result = (await response.json());
954
+ if (!result.success) {
955
+ console.error('Context retrieval failed:', result.message);
956
+ return '';
957
+ }
958
+ return result.context;
959
+ }
960
+ catch (error) {
961
+ console.error('Error retrieving context:', error);
962
+ return '';
963
+ }
964
+ }
965
+ /**
966
+ * Get relevant chunks for a query (for debugging/inspection)
967
+ */
968
+ async getRelevantChunks(query) {
969
+ if (!this.isInitialized || this.chunks.length === 0) {
970
+ return [];
971
+ }
972
+ try {
973
+ const response = await promptbookFetch('/api/knowledge/retrieve-context', {
974
+ method: 'POST',
975
+ headers: {
976
+ 'Content-Type': 'application/json',
977
+ },
978
+ body: JSON.stringify({
979
+ query,
980
+ chunks: this.chunks,
981
+ config: {
982
+ maxRetrievedChunks: this.config.maxRetrievedChunks,
983
+ minRelevanceScore: this.config.minRelevanceScore,
984
+ },
985
+ }),
986
+ });
987
+ if (!response.ok) {
988
+ return [];
989
+ }
990
+ const result = (await response.json());
991
+ return result.success ? result.relevantChunks : [];
992
+ }
993
+ catch (error) {
994
+ console.error('Error retrieving relevant chunks:', error);
995
+ return [];
996
+ }
997
+ }
998
+ /**
999
+ * Get knowledge base statistics
1000
+ */
1001
+ getStats() {
1002
+ return {
1003
+ sources: this.sources.length,
1004
+ chunks: this.chunks.length,
1005
+ isInitialized: this.isInitialized,
1006
+ };
1007
+ }
1008
+ /**
1009
+ * Check if the service is ready to use
1010
+ */
1011
+ isReady() {
1012
+ return this.isInitialized;
1013
+ }
1014
+ /**
1015
+ * Clear all knowledge sources
1016
+ */
1017
+ clearKnowledgeBase() {
1018
+ this.chunks = [];
1019
+ this.sources = [];
1020
+ this.isInitialized = false;
1021
+ }
1022
+ /**
1023
+ * Add a single knowledge source (for incremental updates)
1024
+ */
1025
+ async addKnowledgeSource(url) {
1026
+ if (this.sources.includes(url)) {
1027
+ console.log(`Knowledge source already exists: ${url}`);
1028
+ return;
1029
+ }
1030
+ try {
1031
+ const response = await promptbookFetch('/api/knowledge/process-sources', {
1032
+ method: 'POST',
1033
+ headers: {
1034
+ 'Content-Type': 'application/json',
1035
+ },
1036
+ body: JSON.stringify({
1037
+ sources: [url],
1038
+ config: {
1039
+ maxChunkSize: this.config.maxChunkSize,
1040
+ chunkOverlap: this.config.chunkOverlap,
1041
+ },
1042
+ }),
1043
+ });
1044
+ if (!response.ok) {
1045
+ throw new Error(`Failed to process knowledge source: ${response.status}`);
1046
+ }
1047
+ const result = (await response.json());
1048
+ if (!result.success) {
1049
+ throw new Error(result.message || 'Failed to process knowledge source');
1050
+ }
1051
+ // Add new chunks to existing ones
1052
+ this.chunks.push(...result.chunks);
1053
+ this.sources.push(url);
1054
+ console.log(`Added knowledge source: ${url} (${result.chunks.length} chunks)`);
1055
+ }
1056
+ catch (error) {
1057
+ console.error(`Failed to add knowledge source ${url}:`, error);
1058
+ throw error;
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ /**
1064
+ * KNOWLEDGE commitment definition
1065
+ *
1066
+ * The KNOWLEDGE commitment adds specific knowledge, facts, or context to the agent
1067
+ * using RAG (Retrieval-Augmented Generation) approach for external sources.
1068
+ *
1069
+ * Supports both direct text knowledge and external sources like PDFs.
1070
+ *
1071
+ * Example usage in agent source:
1072
+ *
1073
+ * ```book
1074
+ * KNOWLEDGE The company was founded in 2020 and specializes in AI-powered solutions
1075
+ * KNOWLEDGE https://example.com/company-handbook.pdf
1076
+ * KNOWLEDGE https://example.com/product-documentation.pdf
1077
+ * ```
1078
+ *
1079
+ * @private [🪔] Maybe export the commitments through some package
1080
+ */
1081
+ class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
1082
+ constructor() {
1083
+ super('KNOWLEDGE');
1084
+ this.ragService = new FrontendRAGService();
1085
+ }
1086
+ /**
1087
+ * Short one-line description of KNOWLEDGE.
1088
+ */
1089
+ get description() {
1090
+ return 'Add domain **knowledge** via direct text or external sources (RAG).';
1091
+ }
1092
+ /**
1093
+ * Markdown documentation for KNOWLEDGE commitment.
1094
+ */
1095
+ get documentation() {
1096
+ return spaceTrim.spaceTrim(`
1097
+ # KNOWLEDGE
1098
+
1099
+ Adds specific knowledge, facts, or context to the agent using a RAG (Retrieval-Augmented Generation) approach for external sources.
1100
+
1101
+ ## Key behaviors
1102
+
1103
+ - Multiple \`KNOWLEDGE\` commitments are applied sequentially.
1104
+ - Supports both direct text knowledge and external URLs.
1105
+ - External sources (PDFs, websites) are processed via RAG for context retrieval.
1106
+
1107
+ ## Supported formats
1108
+
1109
+ - Direct text: Immediate knowledge incorporated into agent
1110
+ - URLs: External documents processed for contextual retrieval
1111
+ - Supported file types: PDF, text, markdown, HTML
1112
+
1113
+ ## Examples
1114
+
1115
+ \`\`\`book
1116
+ Customer Support Bot
1117
+
1118
+ PERSONA You are a helpful customer support agent for TechCorp
1119
+ KNOWLEDGE TechCorp was founded in 2020 and specializes in AI-powered solutions
1120
+ KNOWLEDGE https://example.com/company-handbook.pdf
1121
+ KNOWLEDGE https://example.com/product-documentation.pdf
1122
+ RULE Always be polite and professional
1123
+ \`\`\`
1124
+
1125
+ \`\`\`book
1126
+ Research Assistant
1127
+
1128
+ PERSONA You are a knowledgeable research assistant
1129
+ KNOWLEDGE Academic research requires careful citation and verification
1130
+ KNOWLEDGE https://example.com/research-guidelines.pdf
1131
+ ACTION Can help with literature reviews and data analysis
1132
+ STYLE Present information in clear, academic format
1133
+ \`\`\`
1134
+ `);
1135
+ }
1136
+ applyToAgentModelRequirements(requirements, content) {
1137
+ var _a;
1138
+ const trimmedContent = content.trim();
1139
+ if (!trimmedContent) {
1140
+ return requirements;
1141
+ }
1142
+ // Check if content is a URL (external knowledge source)
1143
+ if (this.isUrl(trimmedContent)) {
1144
+ // Store the URL for later async processing
1145
+ const updatedRequirements = {
1146
+ ...requirements,
1147
+ metadata: {
1148
+ ...requirements.metadata,
1149
+ ragService: this.ragService,
1150
+ knowledgeSources: [
1151
+ ...(((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.knowledgeSources) || []),
1152
+ trimmedContent,
1153
+ ],
1154
+ },
1155
+ };
1156
+ // Add placeholder information about knowledge sources to system message
1157
+ const knowledgeInfo = `Knowledge Source URL: ${trimmedContent} (will be processed for retrieval during chat)`;
1158
+ return this.appendToSystemMessage(updatedRequirements, knowledgeInfo, '\n\n');
1159
+ }
1160
+ else {
1161
+ // Direct text knowledge - add to system message
1162
+ const knowledgeSection = `Knowledge: ${trimmedContent}`;
1163
+ return this.appendToSystemMessage(requirements, knowledgeSection, '\n\n');
1164
+ }
1165
+ }
1166
+ /**
1167
+ * Check if content is a URL
1168
+ */
1169
+ isUrl(content) {
1170
+ try {
1171
+ new URL(content);
1172
+ return true;
1173
+ }
1174
+ catch (_a) {
1175
+ return false;
1176
+ }
1177
+ }
1178
+ /**
1179
+ * Get RAG service instance for retrieving context during chat
1180
+ */
1181
+ getRagService() {
1182
+ return this.ragService;
1183
+ }
1184
+ }
1185
+ /**
1186
+ * Singleton instance of the KNOWLEDGE commitment definition
1187
+ *
1188
+ * @private [🪔] Maybe export the commitments through some package
1189
+ */
1190
+ new KnowledgeCommitmentDefinition();
1191
+ /**
1192
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1193
+ */
1194
+
1195
+ /**
1196
+ * META IMAGE commitment definition
1197
+ *
1198
+ * The META IMAGE commitment sets the agent's avatar/profile image URL.
1199
+ * This commitment is special because it doesn't affect the system message,
1200
+ * but is handled separately in the parsing logic.
1201
+ *
1202
+ * Example usage in agent source:
1203
+ *
1204
+ * ```book
1205
+ * META IMAGE https://example.com/avatar.jpg
1206
+ * META IMAGE /assets/agent-avatar.png
1207
+ * ```
1208
+ *
1209
+ * @private [🪔] Maybe export the commitments through some package
1210
+ */
1211
+ class MetaImageCommitmentDefinition extends BaseCommitmentDefinition {
1212
+ constructor() {
1213
+ super('META IMAGE');
1214
+ }
1215
+ /**
1216
+ * Short one-line description of META IMAGE.
1217
+ */
1218
+ get description() {
1219
+ return "Set the agent's profile image URL.";
1220
+ }
1221
+ /**
1222
+ * Markdown documentation for META IMAGE commitment.
1223
+ */
1224
+ get documentation() {
1225
+ return spaceTrim.spaceTrim(`
1226
+ # META IMAGE
1227
+
1228
+ Sets the agent's avatar/profile image URL.
1229
+
1230
+ ## Key behaviors
1231
+
1232
+ - Does not modify the agent's behavior or responses.
1233
+ - Only one \`META IMAGE\` should be used per agent.
1234
+ - If multiple are specified, the last one takes precedence.
1235
+ - Used for visual representation in user interfaces.
1236
+
1237
+ ## Examples
1238
+
1239
+ \`\`\`book
1240
+ Professional Assistant
1241
+
1242
+ META IMAGE https://example.com/professional-avatar.jpg
1243
+ PERSONA You are a professional business assistant
1244
+ STYLE Maintain a formal and courteous tone
1245
+ \`\`\`
1246
+
1247
+ \`\`\`book
1248
+ Creative Helper
1249
+
1250
+ META IMAGE /assets/creative-bot-avatar.png
1251
+ PERSONA You are a creative and inspiring assistant
1252
+ STYLE Be enthusiastic and encouraging
1253
+ ACTION Can help with brainstorming and ideation
1254
+ \`\`\`
1255
+ `);
1256
+ }
1257
+ applyToAgentModelRequirements(requirements, content) {
1258
+ // META IMAGE doesn't modify the system message or model requirements
1259
+ // It's handled separately in the parsing logic for profile image extraction
1260
+ // This method exists for consistency with the CommitmentDefinition interface
1261
+ return requirements;
1262
+ }
1263
+ /**
1264
+ * Extracts the profile image URL from the content
1265
+ * This is used by the parsing logic
1266
+ */
1267
+ extractProfileImageUrl(content) {
1268
+ const trimmedContent = content.trim();
1269
+ return trimmedContent || null;
1270
+ }
1271
+ }
1272
+ /**
1273
+ * Singleton instance of the META IMAGE commitment definition
1274
+ *
1275
+ * @private [🪔] Maybe export the commitments through some package
1276
+ */
1277
+ new MetaImageCommitmentDefinition();
1278
+ /**
1279
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1280
+ */
1281
+
1282
+ /**
1283
+ * META LINK commitment definition
1284
+ *
1285
+ * The `META LINK` commitment represents the link to the person from whom the agent is created.
1286
+ * This commitment is special because it doesn't affect the system message,
1287
+ * but is handled separately in the parsing logic for profile display.
1288
+ *
1289
+ * Example usage in agent source:
1290
+ *
1291
+ * ```
1292
+ * META LINK https://twitter.com/username
1293
+ * META LINK https://linkedin.com/in/profile
1294
+ * META LINK https://github.com/username
1295
+ * ```
1296
+ *
1297
+ * Multiple `META LINK` commitments can be used when there are multiple sources:
1298
+ *
1299
+ * ```book
1300
+ * META LINK https://twitter.com/username
1301
+ * META LINK https://linkedin.com/in/profile
1302
+ * ```
1303
+ *
1304
+ * @private [🪔] Maybe export the commitments through some package
1305
+ */
1306
+ class MetaLinkCommitmentDefinition extends BaseCommitmentDefinition {
1307
+ constructor() {
1308
+ super('META LINK');
1309
+ }
1310
+ /**
1311
+ * Short one-line description of META LINK.
1312
+ */
1313
+ get description() {
1314
+ return 'Provide profile/source links for the person the agent models.';
1315
+ }
1316
+ /**
1317
+ * Markdown documentation for META LINK commitment.
1318
+ */
1319
+ get documentation() {
1320
+ return spaceTrim.spaceTrim(`
1321
+ # META LINK
1322
+
1323
+ Represents a profile or source link for the person the agent is modeled after.
1324
+
1325
+ ## Key behaviors
1326
+
1327
+ - Does not modify the agent's behavior or responses.
1328
+ - Multiple \`META LINK\` commitments can be used for different social profiles.
1329
+ - Used for attribution and crediting the original person.
1330
+ - Displayed in user interfaces for transparency.
1331
+
1332
+ ## Examples
1333
+
1334
+ \`\`\`book
1335
+ Expert Consultant
1336
+
1337
+ META LINK https://twitter.com/expertname
1338
+ META LINK https://linkedin.com/in/expertprofile
1339
+ PERSONA You are Dr. Smith, a renowned expert in artificial intelligence
1340
+ KNOWLEDGE Extensive background in machine learning and neural networks
1341
+ \`\`\`
1342
+
1343
+ \`\`\`book
1344
+ Open Source Developer
1345
+
1346
+ META LINK https://github.com/developer
1347
+ META LINK https://twitter.com/devhandle
1348
+ PERSONA You are an experienced open source developer
1349
+ ACTION Can help with code reviews and architecture decisions
1350
+ STYLE Be direct and technical in explanations
1351
+ \`\`\`
1352
+ `);
1353
+ }
1354
+ applyToAgentModelRequirements(requirements, content) {
1355
+ // META LINK doesn't modify the system message or model requirements
1356
+ // It's handled separately in the parsing logic for profile link extraction
1357
+ // This method exists for consistency with the CommitmentDefinition interface
1358
+ return requirements;
1359
+ }
1360
+ /**
1361
+ * Extracts the profile link URL from the content
1362
+ * This is used by the parsing logic
1363
+ */
1364
+ extractProfileLinkUrl(content) {
1365
+ const trimmedContent = content.trim();
1366
+ return trimmedContent || null;
1367
+ }
1368
+ /**
1369
+ * Validates if the provided content is a valid URL
1370
+ */
1371
+ isValidUrl(content) {
1372
+ try {
1373
+ new URL(content.trim());
1374
+ return true;
1375
+ }
1376
+ catch (_a) {
1377
+ return false;
1378
+ }
1379
+ }
1380
+ }
1381
+ /**
1382
+ * Singleton instance of the META LINK commitment definition
1383
+ *
1384
+ * @private [🪔] Maybe export the commitments through some package
1385
+ */
1386
+ new MetaLinkCommitmentDefinition();
1387
+ /**
1388
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1389
+ */
1390
+
1391
+ /**
1392
+ * MODEL commitment definition
1393
+ *
1394
+ * The MODEL commitment specifies which AI model to use and can also set
1395
+ * model-specific parameters like temperature, topP, and topK.
1396
+ *
1397
+ * Example usage in agent source:
1398
+ *
1399
+ * ```book
1400
+ * MODEL gpt-4
1401
+ * MODEL claude-3-opus temperature=0.3
1402
+ * MODEL gpt-3.5-turbo temperature=0.8 topP=0.9
1403
+ * ```
1404
+ *
1405
+ * @private [🪔] Maybe export the commitments through some package
1406
+ */
1407
+ class ModelCommitmentDefinition extends BaseCommitmentDefinition {
1408
+ constructor() {
1409
+ super('MODEL');
1410
+ }
1411
+ /**
1412
+ * Short one-line description of MODEL.
1413
+ */
1414
+ get description() {
1415
+ return 'Select the AI model and optional decoding parameters.';
1416
+ }
1417
+ /**
1418
+ * Markdown documentation for MODEL commitment.
1419
+ */
1420
+ get documentation() {
1421
+ return spaceTrim.spaceTrim(`
1422
+ # MODEL
1423
+
1424
+ Specifies which AI model to use and optional decoding parameters.
1425
+
1426
+ ## Key behaviors
1427
+
1428
+ - Only one \`MODEL\` commitment should be used per agent.
1429
+ - If multiple are specified, the last one takes precedence.
1430
+ - Parameters control the randomness and creativity of responses.
1431
+
1432
+ ## Supported parameters
1433
+
1434
+ - \`temperature\`: Controls randomness (0.0 = deterministic, 1.0+ = creative)
1435
+ - \`topP\` (aka \`top_p\`): Nucleus sampling parameter
1436
+ - \`topK\` (aka \`top_k\`): Top-k sampling parameter
1437
+
1438
+ ## Examples
1439
+
1440
+ \`\`\`book
1441
+ Precise Assistant
1442
+
1443
+ PERSONA You are a precise and accurate assistant
1444
+ MODEL gpt-4 temperature=0.1
1445
+ RULE Always provide factual information
1446
+ \`\`\`
1447
+
1448
+ \`\`\`book
1449
+ Creative Writer
1450
+
1451
+ PERSONA You are a creative writing assistant
1452
+ MODEL claude-3-opus temperature=0.8 topP=0.9
1453
+ STYLE Be imaginative and expressive
1454
+ ACTION Can help with storytelling and character development
1455
+ \`\`\`
1456
+ `);
1457
+ }
1458
+ applyToAgentModelRequirements(requirements, content) {
1459
+ const trimmedContent = content.trim();
1460
+ if (!trimmedContent) {
1461
+ return requirements;
1462
+ }
1463
+ // Parse the model specification
1464
+ const parts = trimmedContent.split(/\s+/);
1465
+ const modelName = parts[0];
1466
+ if (!modelName) {
1467
+ return requirements;
1468
+ }
1469
+ // Start with the model name
1470
+ const updatedRequirements = {
1471
+ ...requirements,
1472
+ modelName,
1473
+ };
1474
+ // Parse additional parameters
1475
+ const result = { ...updatedRequirements };
1476
+ for (let i = 1; i < parts.length; i++) {
1477
+ const param = parts[i];
1478
+ if (param && param.includes('=')) {
1479
+ const [key, value] = param.split('=');
1480
+ if (key && value) {
1481
+ const numValue = parseFloat(value);
1482
+ if (!isNaN(numValue)) {
1483
+ switch (key.toLowerCase()) {
1484
+ case 'temperature':
1485
+ result.temperature = numValue;
1486
+ break;
1487
+ case 'topp':
1488
+ case 'top_p':
1489
+ result.topP = numValue;
1490
+ break;
1491
+ case 'topk':
1492
+ case 'top_k':
1493
+ result.topK = Math.round(numValue);
1494
+ break;
1495
+ }
1496
+ }
1497
+ }
1498
+ }
1499
+ }
1500
+ return result;
1501
+ }
1502
+ }
1503
+ /**
1504
+ * Singleton instance of the MODEL commitment definition
1505
+ *
1506
+ * @private [🪔] Maybe export the commitments through some package
1507
+ */
1508
+ new ModelCommitmentDefinition();
1509
+ /**
1510
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1511
+ */
1512
+
1513
+ /**
1514
+ * NOTE commitment definition
1515
+ *
1516
+ * The NOTE commitment is used to add comments to the agent source without making any changes
1517
+ * to the system message or agent model requirements. It serves as a documentation mechanism
1518
+ * for developers to add explanatory comments, reminders, or annotations directly in the agent source.
1519
+ *
1520
+ * Key features:
1521
+ * - Makes no changes to the system message
1522
+ * - Makes no changes to agent model requirements
1523
+ * - Content is preserved in metadata.NOTE for debugging and inspection
1524
+ * - Multiple NOTE commitments are aggregated together
1525
+ * - Comments (# NOTE) are removed from the final system message
1526
+ *
1527
+ * Example usage in agent source:
1528
+ *
1529
+ * ```book
1530
+ * NOTE This agent was designed for customer support scenarios
1531
+ * NOTE Remember to update the knowledge base monthly
1532
+ * NOTE Performance optimized for quick response times
1533
+ * ```
1534
+ *
1535
+ * The above notes will be stored in metadata but won't affect the agent's behavior.
1536
+ *
1537
+ * @private [🪔] Maybe export the commitments through some package
1538
+ */
1539
+ class NoteCommitmentDefinition extends BaseCommitmentDefinition {
1540
+ constructor() {
1541
+ super('NOTE');
1542
+ }
1543
+ /**
1544
+ * Short one-line description of NOTE.
1545
+ */
1546
+ get description() {
1547
+ return 'Add developer-facing notes without changing behavior or output.';
1548
+ }
1549
+ /**
1550
+ * Markdown documentation for NOTE commitment.
1551
+ */
1552
+ get documentation() {
1553
+ return spaceTrim.spaceTrim(`
1554
+ # NOTE
1555
+
1556
+ Adds comments for documentation without changing agent behavior.
1557
+
1558
+ ## Key behaviors
1559
+
1560
+ - Does not modify the agent's behavior or responses.
1561
+ - Multiple \`NOTE\` commitments are aggregated for debugging.
1562
+ - Useful for documenting design decisions and reminders.
1563
+ - Content is preserved in metadata for inspection.
1564
+
1565
+ ## Examples
1566
+
1567
+ \`\`\`book
1568
+ Customer Support Bot
1569
+
1570
+ NOTE This agent was designed for customer support scenarios
1571
+ NOTE Remember to update the knowledge base monthly
1572
+ PERSONA You are a helpful customer support representative
1573
+ KNOWLEDGE Company policies and procedures
1574
+ RULE Always be polite and professional
1575
+ \`\`\`
1576
+
1577
+ \`\`\`book
1578
+ Research Assistant
1579
+
1580
+ NOTE Performance optimized for quick response times
1581
+ NOTE Uses RAG for accessing latest research papers
1582
+ PERSONA You are a knowledgeable research assistant
1583
+ ACTION Can help with literature reviews and citations
1584
+ STYLE Present information in academic format
1585
+ \`\`\`
1586
+ `);
1587
+ }
1588
+ applyToAgentModelRequirements(requirements, content) {
1589
+ var _a;
1590
+ // The NOTE commitment makes no changes to the system message or model requirements
1591
+ // It only stores the note content in metadata for documentation purposes
1592
+ const trimmedContent = content.trim();
1593
+ if (!trimmedContent) {
1594
+ return requirements;
1595
+ }
1596
+ // Get existing note content from metadata
1597
+ const existingNoteContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.NOTE) || '';
1598
+ // Merge the new content with existing note content
1599
+ // When multiple NOTE commitments exist, they are aggregated together
1600
+ const mergedNoteContent = existingNoteContent ? `${existingNoteContent}\n${trimmedContent}` : trimmedContent;
1601
+ // Store the merged note content in metadata for debugging and inspection
1602
+ const updatedMetadata = {
1603
+ ...requirements.metadata,
1604
+ NOTE: mergedNoteContent,
1605
+ };
1606
+ // Return requirements with updated metadata but no changes to system message
1607
+ return {
1608
+ ...requirements,
1609
+ metadata: updatedMetadata,
1610
+ };
1611
+ }
1612
+ }
1613
+ /**
1614
+ * Singleton instance of the NOTE commitment definition
1615
+ *
1616
+ * @private [🪔] Maybe export the commitments through some package
1617
+ */
1618
+ new NoteCommitmentDefinition();
1619
+ /**
1620
+ * [💞] Ignore a discrepancy between file name and entity name
1621
+ */
1622
+
1623
+ /**
1624
+ * PERSONA commitment definition
1625
+ *
1626
+ * The PERSONA commitment modifies the agent's personality and character in the system message.
1627
+ * It defines who the agent is, their background, expertise, and personality traits.
1628
+ *
1629
+ * Key features:
1630
+ * - Multiple PERSONA commitments are automatically merged into one
1631
+ * - Content is placed at the beginning of the system message
1632
+ * - Original content with comments is preserved in metadata.PERSONA
1633
+ * - Comments (# PERSONA) are removed from the final system message
1634
+ *
1635
+ * Example usage in agent source:
1636
+ *
1637
+ * ```book
1638
+ * PERSONA You are a helpful programming assistant with expertise in TypeScript and React
1639
+ * PERSONA You have deep knowledge of modern web development practices
1640
+ * ```
1641
+ *
1642
+ * The above will be merged into a single persona section at the beginning of the system message.
1643
+ *
1644
+ * @private [🪔] Maybe export the commitments through some package
1645
+ */
1646
+ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
1647
+ constructor() {
1648
+ super('PERSONA');
1649
+ }
1650
+ /**
1651
+ * Short one-line description of PERSONA.
1652
+ */
1653
+ get description() {
1654
+ return 'Define who the agent is: background, expertise, and personality.';
1655
+ }
1656
+ /**
1657
+ * Markdown documentation for PERSONA commitment.
1658
+ */
1659
+ get documentation() {
1660
+ return spaceTrim.spaceTrim(`
1661
+ # PERSONA
1662
+
1663
+ Defines who the agent is, their background, expertise, and personality traits.
1664
+
1665
+ ## Key behaviors
1666
+
1667
+ - Multiple \`PERSONA\` commitments are merged together.
1668
+ - If they are in conflict, the last one takes precedence.
1669
+ - You can write persona content in multiple lines.
1670
+
1671
+ ## Examples
1672
+
1673
+ \`\`\`book
1674
+ Programming Assistant
1675
+
1676
+ PERSONA You are a helpful programming assistant with expertise in TypeScript and React
1677
+ PERSONA You have deep knowledge of modern web development practices
1678
+ \`\`\`
1679
+ `);
1680
+ }
1681
+ applyToAgentModelRequirements(requirements, content) {
1682
+ var _a, _b;
1683
+ // The PERSONA commitment aggregates all persona content and places it at the beginning
1684
+ const trimmedContent = content.trim();
1685
+ if (!trimmedContent) {
1686
+ return requirements;
1687
+ }
1688
+ // Get existing persona content from metadata
1689
+ const existingPersonaContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.PERSONA) || '';
1690
+ // Merge the new content with existing persona content
1691
+ // When multiple PERSONA commitments exist, they are merged into one
1692
+ const mergedPersonaContent = existingPersonaContent
1693
+ ? `${existingPersonaContent}\n${trimmedContent}`
1694
+ : trimmedContent;
1695
+ // Store the merged persona content in metadata for debugging and inspection
1696
+ const updatedMetadata = {
1697
+ ...requirements.metadata,
1698
+ PERSONA: mergedPersonaContent,
1699
+ };
1700
+ // Get the agent name from metadata (which should contain the first line of agent source)
1701
+ // If not available, extract from current system message as fallback
1702
+ let agentName = (_b = requirements.metadata) === null || _b === void 0 ? void 0 : _b.agentName;
1703
+ if (!agentName) {
1704
+ // Fallback: extract from current system message
1705
+ const currentMessage = requirements.systemMessage.trim();
1706
+ const basicFormatMatch = currentMessage.match(/^You are (.+)$/);
1707
+ if (basicFormatMatch && basicFormatMatch[1]) {
1708
+ agentName = basicFormatMatch[1];
1709
+ }
1710
+ else {
1711
+ agentName = 'AI Agent'; // Final fallback
1712
+ }
1713
+ }
1714
+ // Remove any existing persona content from the system message
1715
+ // (this handles the case where we're processing multiple PERSONA commitments)
1716
+ const currentMessage = requirements.systemMessage.trim();
1717
+ let cleanedMessage = currentMessage;
1718
+ // Check if current message starts with persona content or is just the basic format
1719
+ const basicFormatRegex = /^You are .+$/;
1720
+ const isBasicFormat = basicFormatRegex.test(currentMessage) && !currentMessage.includes('\n');
1721
+ if (isBasicFormat) {
1722
+ // Replace the basic format entirely
1723
+ cleanedMessage = '';
1724
+ }
1725
+ else if (currentMessage.startsWith('# PERSONA')) {
1726
+ // Remove existing persona section by finding where it ends
1727
+ const lines = currentMessage.split('\n');
1728
+ let personaEndIndex = lines.length;
1729
+ // Find the end of the PERSONA section (next comment or end of message)
1730
+ for (let i = 1; i < lines.length; i++) {
1731
+ const line = lines[i].trim();
1732
+ if (line.startsWith('#') && !line.startsWith('# PERSONA')) {
1733
+ personaEndIndex = i;
1734
+ break;
1735
+ }
1736
+ }
1737
+ // Keep everything after the PERSONA section
1738
+ cleanedMessage = lines.slice(personaEndIndex).join('\n').trim();
1739
+ }
1740
+ // Create new system message with persona at the beginning
1741
+ // Format: "You are {agentName}\n{personaContent}"
1742
+ // The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
1743
+ const personaSection = `# PERSONA\nYou are ${agentName}\n${mergedPersonaContent}`; // <- TODO: Use spaceTrim
1744
+ const newSystemMessage = cleanedMessage ? `${personaSection}\n\n${cleanedMessage}` : personaSection;
1745
+ return {
1746
+ ...requirements,
1747
+ systemMessage: newSystemMessage,
1748
+ metadata: updatedMetadata,
1749
+ };
1750
+ }
1751
+ }
1752
+ /**
1753
+ * Singleton instance of the PERSONA commitment definition
1754
+ *
1755
+ * @private [🪔] Maybe export the commitments through some package
1756
+ */
1757
+ new PersonaCommitmentDefinition();
1758
+ /**
1759
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1760
+ */
1761
+
1762
+ /**
1763
+ * RULE commitment definition
1764
+ *
1765
+ * The RULE/RULES commitment adds behavioral constraints and guidelines that the agent must follow.
1766
+ * These are specific instructions about what the agent should or shouldn't do.
1767
+ *
1768
+ * Example usage in agent source:
1769
+ *
1770
+ * ```book
1771
+ * RULE Always ask for clarification if the user's request is ambiguous
1772
+ * RULES Never provide medical advice, always refer to healthcare professionals
1773
+ * ```
1774
+ *
1775
+ * @private [🪔] Maybe export the commitments through some package
1776
+ */
1777
+ class RuleCommitmentDefinition extends BaseCommitmentDefinition {
1778
+ constructor(type = 'RULE') {
1779
+ super(type);
1780
+ }
1781
+ /**
1782
+ * Short one-line description of RULE/RULES.
1783
+ */
1784
+ get description() {
1785
+ return 'Add behavioral rules the agent must follow.';
1786
+ }
1787
+ /**
1788
+ * Markdown documentation for RULE/RULES commitment.
1789
+ */
1790
+ get documentation() {
1791
+ return spaceTrim.spaceTrim(`
1792
+ # ${this.type}
1793
+
1794
+ Adds behavioral constraints and guidelines that the agent must follow.
1795
+
1796
+ ## Key behaviors
1797
+
1798
+ - Multiple \`RULE\` and \`RULES\` commitments are applied sequentially.
1799
+ - All rules are treated equally regardless of singular/plural form.
1800
+ - Rules define what the agent must or must not do.
1801
+
1802
+ ## Examples
1803
+
1804
+ \`\`\`book
1805
+ Customer Support Agent
1806
+
1807
+ PERSONA You are a helpful customer support representative
1808
+ RULE Always ask for clarification if the user's request is ambiguous
1809
+ RULE Be polite and professional in all interactions
1810
+ RULES Never provide medical or legal advice
1811
+ STYLE Maintain a friendly and helpful tone
1812
+ \`\`\`
1813
+
1814
+ \`\`\`book
1815
+ Educational Tutor
1816
+
1817
+ PERSONA You are a patient and knowledgeable tutor
1818
+ RULE Break down complex concepts into simple steps
1819
+ RULE Always encourage students and celebrate their progress
1820
+ RULE If you don't know something, admit it and suggest resources
1821
+ SAMPLE When explaining math: "Let's work through this step by step..."
1822
+ \`\`\`
1823
+ `);
1824
+ }
1825
+ applyToAgentModelRequirements(requirements, content) {
1826
+ const trimmedContent = content.trim();
1827
+ if (!trimmedContent) {
1828
+ return requirements;
1829
+ }
1830
+ // Add rule to the system message
1831
+ const ruleSection = `Rule: ${trimmedContent}`;
1832
+ return this.appendToSystemMessage(requirements, ruleSection, '\n\n');
1833
+ }
1834
+ }
1835
+ /**
1836
+ * Singleton instances of the RULE commitment definitions
1837
+ *
1838
+ * @private [🪔] Maybe export the commitments through some package
1839
+ */
1840
+ new RuleCommitmentDefinition('RULE');
1841
+ /**
1842
+ * Singleton instances of the RULE commitment definitions
1843
+ *
1844
+ * @private [🪔] Maybe export the commitments through some package
1845
+ */
1846
+ new RuleCommitmentDefinition('RULES');
1847
+ /**
1848
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1849
+ */
1850
+
1851
+ /**
1852
+ * SAMPLE commitment definition
1853
+ *
1854
+ * The SAMPLE/EXAMPLE commitment provides examples of how the agent should respond
1855
+ * or behave in certain situations. These examples help guide the agent's responses.
1856
+ *
1857
+ * Example usage in agent source:
1858
+ *
1859
+ * ```book
1860
+ * SAMPLE When asked about pricing, respond: "Our basic plan starts at $10/month..."
1861
+ * EXAMPLE For code questions, always include working code snippets
1862
+ * ```
1863
+ *
1864
+ * @private [🪔] Maybe export the commitments through some package
1865
+ */
1866
+ class SampleCommitmentDefinition extends BaseCommitmentDefinition {
1867
+ constructor(type = 'SAMPLE') {
1868
+ super(type);
1869
+ }
1870
+ /**
1871
+ * Short one-line description of SAMPLE/EXAMPLE.
1872
+ */
1873
+ get description() {
1874
+ return 'Provide example responses to guide behavior.';
1875
+ }
1876
+ /**
1877
+ * Markdown documentation for SAMPLE/EXAMPLE commitment.
1878
+ */
1879
+ get documentation() {
1880
+ return spaceTrim.spaceTrim(`
1881
+ # ${this.type}
1882
+
1883
+ Provides examples of how the agent should respond or behave in certain situations.
1884
+
1885
+ ## Key behaviors
1886
+
1887
+ - Multiple \`SAMPLE\` and \`EXAMPLE\` commitments are applied sequentially.
1888
+ - Both terms work identically and can be used interchangeably.
1889
+ - Examples help guide the agent's response patterns and style.
1890
+
1891
+ ## Examples
1892
+
1893
+ \`\`\`book
1894
+ Sales Assistant
1895
+
1896
+ PERSONA You are a knowledgeable sales representative
1897
+ SAMPLE When asked about pricing, respond: "Our basic plan starts at $10/month..."
1898
+ SAMPLE For feature comparisons, create a clear comparison table
1899
+ RULE Always be honest about limitations
1900
+ \`\`\`
1901
+
1902
+ \`\`\`book
1903
+ Code Reviewer
1904
+
1905
+ PERSONA You are an experienced software engineer
1906
+ EXAMPLE For code questions, always include working code snippets
1907
+ EXAMPLE When suggesting improvements: "Here's a more efficient approach..."
1908
+ RULE Explain the reasoning behind your suggestions
1909
+ STYLE Be constructive and encouraging in feedback
1910
+ \`\`\`
1911
+ `);
1912
+ }
1913
+ applyToAgentModelRequirements(requirements, content) {
1914
+ const trimmedContent = content.trim();
1915
+ if (!trimmedContent) {
1916
+ return requirements;
1917
+ }
1918
+ // Add example to the system message
1919
+ const exampleSection = `Example: ${trimmedContent}`;
1920
+ return this.appendToSystemMessage(requirements, exampleSection, '\n\n');
1921
+ }
1922
+ }
1923
+ /**
1924
+ * Singleton instances of the SAMPLE commitment definitions
1925
+ *
1926
+ * @private [🪔] Maybe export the commitments through some package
1927
+ */
1928
+ new SampleCommitmentDefinition('SAMPLE');
1929
+ /**
1930
+ * Singleton instances of the SAMPLE commitment definitions
1931
+ *
1932
+ * @private [🪔] Maybe export the commitments through some package
1933
+ */
1934
+ new SampleCommitmentDefinition('EXAMPLE');
1935
+ /**
1936
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1937
+ */
1938
+
1939
+ /**
1940
+ * STYLE commitment definition
1941
+ *
1942
+ * The STYLE commitment defines how the agent should format and present its responses.
1943
+ * This includes tone, writing style, formatting preferences, and communication patterns.
1944
+ *
1945
+ * Example usage in agent source:
1946
+ *
1947
+ * ```book
1948
+ * STYLE Write in a professional but friendly tone, use bullet points for lists
1949
+ * STYLE Always provide code examples when explaining programming concepts
1950
+ * ```
1951
+ *
1952
+ * @private [🪔] Maybe export the commitments through some package
1953
+ */
1954
+ class StyleCommitmentDefinition extends BaseCommitmentDefinition {
1955
+ constructor() {
1956
+ super('STYLE');
1957
+ }
1958
+ /**
1959
+ * Short one-line description of STYLE.
1960
+ */
1961
+ get description() {
1962
+ return 'Control the tone and writing style of responses.';
1963
+ }
1964
+ /**
1965
+ * Markdown documentation for STYLE commitment.
1966
+ */
1967
+ get documentation() {
1968
+ return spaceTrim.spaceTrim(`
1969
+ # STYLE
1970
+
1971
+ Defines how the agent should format and present its responses (tone, writing style, formatting).
1972
+
1973
+ ## Key behaviors
1974
+
1975
+ - Multiple \`STYLE\` commitments are applied sequentially.
1976
+ - Later style instructions can override earlier ones.
1977
+ - Style affects both tone and presentation format.
1978
+
1979
+ ## Examples
1980
+
1981
+ \`\`\`book
1982
+ Technical Writer
1983
+
1984
+ PERSONA You are a technical documentation expert
1985
+ STYLE Write in a professional but friendly tone, use bullet points for lists
1986
+ STYLE Always provide code examples when explaining programming concepts
1987
+ FORMAT Use markdown formatting with clear headings
1988
+ \`\`\`
1989
+
1990
+ \`\`\`book
1991
+ Creative Assistant
1992
+
1993
+ PERSONA You are a creative writing helper
1994
+ STYLE Be enthusiastic and encouraging in your responses
1995
+ STYLE Use vivid metaphors and analogies to explain concepts
1996
+ STYLE Keep responses conversational and engaging
1997
+ RULE Always maintain a positive and supportive tone
1998
+ \`\`\`
1999
+ `);
2000
+ }
2001
+ applyToAgentModelRequirements(requirements, content) {
2002
+ const trimmedContent = content.trim();
2003
+ if (!trimmedContent) {
2004
+ return requirements;
2005
+ }
2006
+ // Add style instructions to the system message
2007
+ const styleSection = `Style: ${trimmedContent}`;
2008
+ return this.appendToSystemMessage(requirements, styleSection, '\n\n');
2009
+ }
2010
+ }
2011
+ /**
2012
+ * Singleton instance of the STYLE commitment definition
2013
+ *
2014
+ * @private [🪔] Maybe export the commitments through some package
2015
+ */
2016
+ new StyleCommitmentDefinition();
2017
+ /**
2018
+ * [💞] Ignore a discrepancy between file name and entity name
2019
+ */
2020
+
2021
+ /**
2022
+ * Placeholder commitment definition for commitments that are not yet implemented
2023
+ *
2024
+ * This commitment simply adds its content 1:1 into the system message,
2025
+ * preserving the original behavior until proper implementation is added.
2026
+ *
2027
+ * @public exported from `@promptbook/core`
2028
+ */
2029
+ class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
2030
+ constructor(type) {
2031
+ super(type);
2032
+ }
2033
+ /**
2034
+ * Short one-line description of a placeholder commitment.
2035
+ */
2036
+ get description() {
2037
+ return 'Placeholder commitment that appends content verbatim to the system message.';
2038
+ }
2039
+ /**
2040
+ * Markdown documentation available at runtime.
2041
+ */
2042
+ get documentation() {
2043
+ return spaceTrim.spaceTrim(`
2044
+ # ${this.type}
2045
+
2046
+ This commitment is not yet fully implemented.
2047
+
2048
+ ## Key behaviors
2049
+
2050
+ - Content is appended directly to the system message.
2051
+ - No special processing or validation is performed.
2052
+ - Behavior preserved until proper implementation is added.
2053
+
2054
+ ## Status
2055
+
2056
+ - **Status:** Placeholder implementation
2057
+ - **Effect:** Appends content prefixed by commitment type
2058
+ - **Future:** Will be replaced with specialized logic
2059
+
2060
+ ## Examples
2061
+
2062
+ \`\`\`book
2063
+ Example Agent
2064
+
2065
+ PERSONA You are a helpful assistant
2066
+ ${this.type} Your content here
2067
+ RULE Always be helpful
2068
+ \`\`\`
2069
+ `);
2070
+ }
2071
+ applyToAgentModelRequirements(requirements, content) {
2072
+ const trimmedContent = content.trim();
2073
+ if (!trimmedContent) {
2074
+ return requirements;
2075
+ }
2076
+ // Add the commitment content 1:1 to the system message
2077
+ const commitmentLine = `${this.type} ${trimmedContent}`;
2078
+ return this.appendToSystemMessage(requirements, commitmentLine, '\n\n');
2079
+ }
2080
+ }
2081
+
2082
+ // Import all commitment definition classes
2083
+ /**
2084
+ * Registry of all available commitment definitions
2085
+ * This array contains instances of all commitment definitions
2086
+ * This is the single source of truth for all commitments in the system
2087
+ *
2088
+ * @private Use functions to access commitments instead of this array directly
2089
+ */
2090
+ const COMMITMENT_REGISTRY = [
2091
+ // Fully implemented commitments
2092
+ new PersonaCommitmentDefinition(),
2093
+ new KnowledgeCommitmentDefinition(),
2094
+ new StyleCommitmentDefinition(),
2095
+ new RuleCommitmentDefinition('RULE'),
2096
+ new RuleCommitmentDefinition('RULES'),
2097
+ new SampleCommitmentDefinition('SAMPLE'),
2098
+ new SampleCommitmentDefinition('EXAMPLE'),
2099
+ new FormatCommitmentDefinition(),
2100
+ new ModelCommitmentDefinition(),
2101
+ new ActionCommitmentDefinition(),
2102
+ new MetaImageCommitmentDefinition(),
2103
+ new MetaLinkCommitmentDefinition(),
2104
+ new NoteCommitmentDefinition(),
2105
+ // Not yet implemented commitments (using placeholder)
2106
+ new NotYetImplementedCommitmentDefinition('EXPECT'),
2107
+ new NotYetImplementedCommitmentDefinition('SCENARIO'),
2108
+ new NotYetImplementedCommitmentDefinition('SCENARIOS'),
2109
+ new NotYetImplementedCommitmentDefinition('BEHAVIOUR'),
2110
+ new NotYetImplementedCommitmentDefinition('BEHAVIOURS'),
2111
+ new NotYetImplementedCommitmentDefinition('AVOID'),
2112
+ new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
2113
+ new NotYetImplementedCommitmentDefinition('GOAL'),
2114
+ new NotYetImplementedCommitmentDefinition('GOALS'),
2115
+ new NotYetImplementedCommitmentDefinition('CONTEXT'),
2116
+ ];
2117
+ /**
2118
+ * Gets a commitment definition by its type
2119
+ * @param type The commitment type to look up
2120
+ * @returns The commitment definition or null if not found
2121
+ *
2122
+ * @public exported from `@promptbook/core`
2123
+ */
2124
+ function getCommitmentDefinition(type) {
2125
+ return COMMITMENT_REGISTRY.find((commitmentDefinition) => commitmentDefinition.type === type) || null;
2126
+ }
2127
+ /**
2128
+ * Gets all available commitment definitions
2129
+ * @returns Array of all commitment definitions
2130
+ *
2131
+ * @public exported from `@promptbook/core`
2132
+ */
2133
+ function getAllCommitmentDefinitions() {
2134
+ return $deepFreeze([...COMMITMENT_REGISTRY]);
2135
+ }
2136
+ /**
2137
+ * Gets all available commitment types
2138
+ * @returns Array of all commitment types
2139
+ *
2140
+ * @public exported from `@promptbook/core`
2141
+ */
2142
+ function getAllCommitmentTypes() {
2143
+ return $deepFreeze(COMMITMENT_REGISTRY.map((commitmentDefinition) => commitmentDefinition.type));
2144
+ }
2145
+ /**
2146
+ * Checks if a commitment type is supported
2147
+ * @param type The commitment type to check
2148
+ * @returns True if the commitment type is supported
2149
+ *
2150
+ * @public exported from `@promptbook/core`
2151
+ */
2152
+ function isCommitmentSupported(type) {
2153
+ return COMMITMENT_REGISTRY.some((commitmentDefinition) => commitmentDefinition.type === type);
2154
+ }
2155
+ /**
2156
+ * TODO: !!!! Maybe create through standardized $register
2157
+ * Note: [💞] Ignore a discrepancy between file name and entity name
2158
+ */
2159
+
2160
+ /**
2161
+ * Parses agent source using the new commitment system with multiline support
2162
+ * This function replaces the hardcoded commitment parsing in the original parseAgentSource
2163
+ *
2164
+ * @private
2165
+ */
2166
+ function parseAgentSourceWithCommitments(agentSource) {
2167
+ var _a, _b, _c;
2168
+ if (!agentSource || !agentSource.trim()) {
2169
+ return {
2170
+ agentName: null,
2171
+ commitments: [],
2172
+ nonCommitmentLines: [],
2173
+ };
2174
+ }
2175
+ const lines = agentSource.split('\n');
2176
+ const agentName = (((_a = lines[0]) === null || _a === void 0 ? void 0 : _a.trim()) || null);
2177
+ const commitments = [];
2178
+ const nonCommitmentLines = [];
2179
+ // Always add the first line (agent name) to non-commitment lines
2180
+ if (lines[0] !== undefined) {
2181
+ nonCommitmentLines.push(lines[0]);
2182
+ }
2183
+ // Parse commitments with multiline support
2184
+ let currentCommitment = null;
2185
+ // Process lines starting from the second line (skip agent name)
2186
+ for (let i = 1; i < lines.length; i++) {
2187
+ const line = lines[i];
2188
+ if (line === undefined) {
2189
+ continue;
2190
+ }
2191
+ // Check if this line starts a new commitment
2192
+ let foundNewCommitment = false;
2193
+ for (const definition of COMMITMENT_REGISTRY) {
2194
+ const typeRegex = definition.createTypeRegex();
2195
+ const match = typeRegex.exec(line.trim());
2196
+ if (match && ((_b = match.groups) === null || _b === void 0 ? void 0 : _b.type)) {
2197
+ // Save the previous commitment if it exists
2198
+ if (currentCommitment) {
2199
+ const fullContent = currentCommitment.contentLines.join('\n');
2200
+ commitments.push({
2201
+ type: currentCommitment.type,
2202
+ content: spaceTrim.spaceTrim(fullContent),
2203
+ originalLine: currentCommitment.originalStartLine,
2204
+ lineNumber: currentCommitment.startLineNumber,
2205
+ });
2206
+ }
2207
+ // Extract the initial content from the commitment line
2208
+ const fullRegex = definition.createRegex();
2209
+ const fullMatch = fullRegex.exec(line.trim());
2210
+ const initialContent = ((_c = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _c === void 0 ? void 0 : _c.contents) || '';
2211
+ // Start a new commitment
2212
+ currentCommitment = {
2213
+ type: definition.type,
2214
+ startLineNumber: i + 1,
2215
+ originalStartLine: line,
2216
+ contentLines: initialContent ? [initialContent] : [],
2217
+ };
2218
+ foundNewCommitment = true;
2219
+ break;
2220
+ }
2221
+ }
2222
+ if (!foundNewCommitment) {
2223
+ if (currentCommitment) {
2224
+ // This line belongs to the current commitment
2225
+ currentCommitment.contentLines.push(line);
2226
+ }
2227
+ else {
2228
+ // This line is not part of any commitment
2229
+ nonCommitmentLines.push(line);
2230
+ }
2231
+ }
2232
+ }
2233
+ // Don't forget to save the last commitment if it exists
2234
+ if (currentCommitment) {
2235
+ const fullContent = currentCommitment.contentLines.join('\n');
2236
+ commitments.push({
2237
+ type: currentCommitment.type,
2238
+ content: spaceTrim.spaceTrim(fullContent),
2239
+ originalLine: currentCommitment.originalStartLine,
2240
+ lineNumber: currentCommitment.startLineNumber,
2241
+ });
2242
+ }
2243
+ return {
2244
+ agentName,
2245
+ commitments,
2246
+ nonCommitmentLines,
2247
+ };
2248
+ }
2249
+ /**
2250
+ * Extracts basic information from agent source using the new commitment system
2251
+ * This maintains compatibility with the original parseAgentSource interface
2252
+ *
2253
+ * @private
2254
+ */
2255
+ function parseAgentSourceBasicInfo(agentSource) {
2256
+ const parseResult = parseAgentSourceWithCommitments(agentSource);
2257
+ // Find PERSONA and META IMAGE commitments
2258
+ let personaDescription = null;
2259
+ let profileImageUrl;
2260
+ for (const commitment of parseResult.commitments) {
2261
+ if (commitment.type === 'PERSONA' && !personaDescription) {
2262
+ personaDescription = commitment.content;
2263
+ }
2264
+ else if (commitment.type === 'META IMAGE' && !profileImageUrl) {
2265
+ profileImageUrl = commitment.content;
2266
+ }
2267
+ }
2268
+ // Generate gravatar fallback if no profile image specified
2269
+ if (!profileImageUrl) {
2270
+ profileImageUrl = generateGravatarUrl(parseResult.agentName);
2271
+ }
2272
+ return {
2273
+ agentName: parseResult.agentName,
2274
+ personaDescription,
2275
+ profileImageUrl,
2276
+ };
2277
+ }
2278
+
2279
+ /**
2280
+ * Parses agent source string into its components
2281
+ */
2282
+ // Cache for parsed agent sources to prevent repeated parsing
2283
+ const parsedAgentSourceCache = new Map();
2284
+ /**
2285
+ * Parses basic information from agent source
2286
+ *
2287
+ * There are 2 similar functions:
2288
+ * - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
2289
+ * - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
2290
+ *
2291
+ * @public exported from `@promptbook/core`
2292
+ */
2293
+ function parseAgentSource(agentSource) {
2294
+ // Check if we already parsed this agent source
2295
+ if (parsedAgentSourceCache.has(agentSource)) {
2296
+ return parsedAgentSourceCache.get(agentSource);
2297
+ }
2298
+ // Use the new commitment-based parsing system
2299
+ const result = parseAgentSourceBasicInfo(agentSource);
2300
+ // Cache the result
2301
+ parsedAgentSourceCache.set(agentSource, result);
2302
+ return result;
2303
+ }
2304
+
2305
+ /**
2306
+ * Type guard to check if a string is a valid agent source
2307
+ *
2308
+ * @public exported from `@promptbook/core`
2309
+ */
2310
+ function isValidBook(value) {
2311
+ // Basic validation - agent source should have at least a name (first line)
2312
+ return typeof value === 'string' /* && value.trim().length > 0 */;
2313
+ }
2314
+ /**
2315
+ * Validates and converts a string to agent source branded type
2316
+ * This function should be used when you have a string that you know represents agent source
2317
+ * but need to convert it to the branded type for type safety
2318
+ *
2319
+ * @public exported from `@promptbook/core`
2320
+ */
2321
+ function validateBook(source) {
2322
+ if (!isValidBook(source)) {
2323
+ throw new Error('Invalid agent source: must be a string');
2324
+ }
2325
+ return source;
2326
+ }
2327
+ /**
2328
+ * Default book
2329
+ *
2330
+ * @public exported from `@promptbook/core`
2331
+ */
2332
+ const DEFAULT_BOOK = validateBook(spaceTrim__default["default"](`
2333
+ AI Avatar
2334
+
2335
+ PERSONA A friendly AI assistant that helps you with your tasks
2336
+ `));
2337
+
2338
+ /**
2339
+ * Creates an empty/basic agent model requirements object
2340
+ * This serves as the starting point for the reduce-like pattern
2341
+ * where each commitment applies its changes to build the final requirements
2342
+ *
2343
+ * @public exported from `@promptbook/core`
2344
+ */
2345
+ function createEmptyAgentModelRequirements() {
2346
+ return {
2347
+ systemMessage: '',
2348
+ modelName: '!!!!DEFAULT_MODEL_ID',
2349
+ temperature: 0.7,
2350
+ topP: 0.9,
2351
+ topK: 50,
2352
+ };
2353
+ }
2354
+ /**
2355
+ * Creates a basic agent model requirements with just the agent name
2356
+ * This is used when we have an agent name but no commitments
2357
+ *
2358
+ * @public exported from `@promptbook/core`
2359
+ */
2360
+ function createBasicAgentModelRequirements(agentName) {
2361
+ const empty = createEmptyAgentModelRequirements();
2362
+ return {
2363
+ ...empty,
2364
+ systemMessage: `You are ${agentName || 'AI Agent'}`,
2365
+ };
2366
+ }
2367
+ /**
2368
+ * TODO: !!!! Deduplicate model requirements
2369
+ */
2370
+
2371
+ /**
2372
+ * Removes comment lines (lines starting with #) from a system message
2373
+ * This is used to clean up the final system message before sending it to the AI model
2374
+ * while preserving the original content with comments in metadata
2375
+ *
2376
+ * @param systemMessage The system message that may contain comment lines
2377
+ * @returns The system message with comment lines removed
2378
+ *
2379
+ * @private - TODO: [🧠] Maybe should be public?
2380
+ */
2381
+ function removeCommentsFromSystemMessage(systemMessage) {
2382
+ if (!systemMessage) {
2383
+ return systemMessage;
2384
+ }
2385
+ const lines = systemMessage.split('\n');
2386
+ const filteredLines = lines.filter((line) => {
2387
+ const trimmedLine = line.trim();
2388
+ // Remove lines that start with # (comments)
2389
+ return !trimmedLine.startsWith('#');
2390
+ });
2391
+ return filteredLines.join('\n').trim();
2392
+ }
2393
+
2394
+ /**
2395
+ * Creates agent model requirements using the new commitment system
2396
+ * This function uses a reduce-like pattern where each commitment applies its changes
2397
+ * to build the final requirements starting from a basic empty model
2398
+ *
2399
+ * @private
2400
+ */
2401
+ async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
2402
+ // Parse the agent source to extract commitments
2403
+ const parseResult = parseAgentSourceWithCommitments(agentSource);
2404
+ // Start with basic agent model requirements
2405
+ let requirements = createBasicAgentModelRequirements(parseResult.agentName);
2406
+ // Store the agent name in metadata so commitments can access it
2407
+ requirements = {
2408
+ ...requirements,
2409
+ metadata: {
2410
+ ...requirements.metadata,
2411
+ agentName: parseResult.agentName,
2412
+ },
2413
+ };
2414
+ // Override model name if provided
2415
+ if (modelName) {
2416
+ requirements = {
2417
+ ...requirements,
2418
+ modelName,
2419
+ };
2420
+ }
2421
+ // Apply each commitment in order using reduce-like pattern
2422
+ for (const commitment of parseResult.commitments) {
2423
+ const definition = getCommitmentDefinition(commitment.type);
2424
+ if (definition) {
2425
+ try {
2426
+ requirements = definition.applyToAgentModelRequirements(requirements, commitment.content);
2427
+ }
2428
+ catch (error) {
2429
+ console.warn(`Failed to apply commitment ${commitment.type}:`, error);
2430
+ // Continue with other commitments even if one fails
2431
+ }
2432
+ }
2433
+ }
2434
+ // Handle MCP servers (extract from original agent source)
2435
+ const mcpServers = extractMcpServers(agentSource);
2436
+ if (mcpServers.length > 0) {
2437
+ requirements = {
2438
+ ...requirements,
2439
+ mcpServers,
2440
+ };
2441
+ }
2442
+ // Add non-commitment lines to system message if they exist
2443
+ const nonCommitmentContent = parseResult.nonCommitmentLines
2444
+ .filter((line, index) => index > 0 || !parseResult.agentName) // Skip first line if it's the agent name
2445
+ .filter((line) => line.trim()) // Remove empty lines
2446
+ .join('\n')
2447
+ .trim();
2448
+ if (nonCommitmentContent) {
2449
+ requirements = {
2450
+ ...requirements,
2451
+ systemMessage: requirements.systemMessage + '\n\n' + nonCommitmentContent,
2452
+ };
2453
+ }
2454
+ // Remove comment lines (lines starting with #) from the final system message
2455
+ // while preserving the original content with comments in metadata
2456
+ const cleanedSystemMessage = removeCommentsFromSystemMessage(requirements.systemMessage);
2457
+ return {
2458
+ ...requirements,
2459
+ systemMessage: cleanedSystemMessage,
2460
+ };
2461
+ }
2462
+ /**
2463
+ * Cache for expensive createAgentModelRequirementsWithCommitments calls
2464
+ * @private
2465
+ */
2466
+ const modelRequirementsCache = new Map();
2467
+ /**
2468
+ * @private - TODO: Maybe should be public
2469
+ */
2470
+ const CACHE_SIZE_LIMIT = 100;
2471
+ /**
2472
+ * Cached version of createAgentModelRequirementsWithCommitments
2473
+ * This maintains the same caching behavior as the original function
2474
+ *
2475
+ * @private
2476
+ */
2477
+ async function createAgentModelRequirementsWithCommitmentsCached(agentSource, modelName) {
2478
+ // Create cache key
2479
+ const cacheKey = `${agentSource}|${modelName || 'default'}`;
2480
+ // Check cache first
2481
+ if (modelRequirementsCache.has(cacheKey)) {
2482
+ return modelRequirementsCache.get(cacheKey);
2483
+ }
2484
+ // Limit cache size to prevent memory leaks
2485
+ if (modelRequirementsCache.size >= CACHE_SIZE_LIMIT) {
2486
+ const firstKey = modelRequirementsCache.keys().next().value;
2487
+ if (firstKey) {
2488
+ modelRequirementsCache.delete(firstKey);
2489
+ }
2490
+ }
2491
+ // Create requirements
2492
+ const requirements = await createAgentModelRequirementsWithCommitments(agentSource, modelName);
2493
+ // Cache the result
2494
+ modelRequirementsCache.set(cacheKey, requirements);
2495
+ return requirements;
2496
+ }
2497
+
2498
+ // TODO: Remove or use:
2499
+ //const CACHE_SIZE_LIMIT = 100; // Prevent memory leaks by limiting cache size
2500
+ /**
2501
+ * Creates model requirements for an agent based on its source
2502
+ * Results are cached to improve performance for repeated calls with the same agentSource and modelName
2503
+ *
2504
+ * There are 2 similar functions:
2505
+ * - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
2506
+ * - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
2507
+ *
2508
+ * @public exported from `@promptbook/core`
2509
+ */
2510
+ async function createAgentModelRequirements(agentSource, modelName = '!!!!DEFAULT_MODEL_ID') {
2511
+ // Use the new commitment-based system
2512
+ return createAgentModelRequirementsWithCommitmentsCached(agentSource, modelName);
2513
+ }
2514
+ /**
2515
+ * Extracts MCP servers from agent source
2516
+ *
2517
+ * @param agentSource The agent source string that may contain MCP lines
2518
+ * @returns Array of MCP server identifiers
2519
+ *
2520
+ * @private TODO: [🧠] Maybe should be public
2521
+ */
2522
+ function extractMcpServers(agentSource) {
2523
+ if (!agentSource) {
2524
+ return [];
2525
+ }
2526
+ const lines = agentSource.split('\n');
2527
+ const mcpRegex = /^\s*MCP\s+(.+)$/i;
2528
+ const mcpServers = [];
2529
+ // Look for MCP lines
2530
+ for (const line of lines) {
2531
+ const match = line.match(mcpRegex);
2532
+ if (match && match[1]) {
2533
+ mcpServers.push(match[1].trim());
2534
+ }
2535
+ }
2536
+ return mcpServers;
2537
+ }
2538
+
2539
+ /**
2540
+ * Converts PipelineCollection to serialized JSON
2541
+ *
2542
+ * Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
557
2543
  *
558
2544
  * @public exported from `@promptbook/core`
559
2545
  */
560
- class UnexpectedError extends Error {
561
- constructor(message) {
562
- super(spaceTrim.spaceTrim((block) => `
563
- ${block(message)}
564
-
565
- Note: This error should not happen.
566
- It's probably a bug in the pipeline collection
567
-
568
- Please report issue:
569
- ${block(getErrorReportUrl(new Error(message)).href)}
570
-
571
- Or contact us on ${ADMIN_EMAIL}
2546
+ async function collectionToJson(collection) {
2547
+ const pipelineUrls = await collection.listPipelines();
2548
+ const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
2549
+ return promptbooks;
2550
+ }
2551
+ /**
2552
+ * TODO: [🧠] Maybe clear `sourceFile` or clear when exposing through API or remote server
2553
+ */
572
2554
 
573
- `));
574
- this.name = 'UnexpectedError';
575
- Object.setPrototypeOf(this, UnexpectedError.prototype);
2555
+ /**
2556
+ * Checks if value is valid email
2557
+ *
2558
+ * @public exported from `@promptbook/utils`
2559
+ */
2560
+ function isValidEmail(email) {
2561
+ if (typeof email !== 'string') {
2562
+ return false;
2563
+ }
2564
+ if (email.split('\n').length > 1) {
2565
+ return false;
576
2566
  }
2567
+ return /^.+@.+\..+$/.test(email);
577
2568
  }
578
2569
 
579
2570
  /**
580
- * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
2571
+ * Tests if given string is valid URL.
581
2572
  *
582
- * @public exported from `@promptbook/core`
2573
+ * Note: This does not check if the file exists only if the path is valid
2574
+ * @public exported from `@promptbook/utils`
583
2575
  */
584
- class WrappedError extends Error {
585
- constructor(whatWasThrown) {
586
- const tag = `[🤮]`;
587
- console.error(tag, whatWasThrown);
588
- super(spaceTrim.spaceTrim(`
589
- Non-Error object was thrown
590
-
591
- Note: Look for ${tag} in the console for more details
592
- Please report issue on ${ADMIN_EMAIL}
593
- `));
594
- this.name = 'WrappedError';
595
- Object.setPrototypeOf(this, WrappedError.prototype);
2576
+ function isValidFilePath(filename) {
2577
+ if (typeof filename !== 'string') {
2578
+ return false;
2579
+ }
2580
+ if (filename.split('\n').length > 1) {
2581
+ return false;
2582
+ }
2583
+ if (filename.split(' ').length >
2584
+ 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
2585
+ return false;
2586
+ }
2587
+ const filenameSlashes = filename.split('\\').join('/');
2588
+ // Absolute Unix path: /hello.txt
2589
+ if (/^(\/)/i.test(filenameSlashes)) {
2590
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
2591
+ return true;
2592
+ }
2593
+ // Absolute Windows path: /hello.txt
2594
+ if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
2595
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
2596
+ return true;
2597
+ }
2598
+ // Relative path: ./hello.txt
2599
+ if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
2600
+ // console.log(filename, 'Relative path: ./hello.txt');
2601
+ return true;
596
2602
  }
2603
+ // Allow paths like foo/hello
2604
+ if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
2605
+ // console.log(filename, 'Allow paths like foo/hello');
2606
+ return true;
2607
+ }
2608
+ // Allow paths like hello.book
2609
+ if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
2610
+ // console.log(filename, 'Allow paths like hello.book');
2611
+ return true;
2612
+ }
2613
+ return false;
597
2614
  }
2615
+ /**
2616
+ * TODO: [🍏] Implement for MacOs
2617
+ */
598
2618
 
599
2619
  /**
600
- * Helper used in catch blocks to assert that the error is an instance of `Error`
2620
+ * Tests if given string is valid URL.
601
2621
  *
602
- * @param whatWasThrown Any object that was thrown
603
- * @returns Nothing if the error is an instance of `Error`
604
- * @throws `WrappedError` or `UnexpectedError` if the error is not standard
2622
+ * Note: Dataurl are considered perfectly valid.
2623
+ * Note: There are two similar functions:
2624
+ * - `isValidUrl` which tests any URL
2625
+ * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
605
2626
  *
606
- * @private within the repository
2627
+ * @public exported from `@promptbook/utils`
607
2628
  */
608
- function assertsError(whatWasThrown) {
609
- // Case 1: Handle error which was rethrown as `WrappedError`
610
- if (whatWasThrown instanceof WrappedError) {
611
- const wrappedError = whatWasThrown;
612
- throw wrappedError;
2629
+ function isValidUrl(url) {
2630
+ if (typeof url !== 'string') {
2631
+ return false;
613
2632
  }
614
- // Case 2: Handle unexpected errors
615
- if (whatWasThrown instanceof UnexpectedError) {
616
- const unexpectedError = whatWasThrown;
617
- throw unexpectedError;
2633
+ try {
2634
+ if (url.startsWith('blob:')) {
2635
+ url = url.replace(/^blob:/, '');
2636
+ }
2637
+ const urlObject = new URL(url /* because fail is handled */);
2638
+ if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
2639
+ return false;
2640
+ }
2641
+ return true;
618
2642
  }
619
- // Case 3: Handle standard errors - keep them up to consumer
620
- if (whatWasThrown instanceof Error) {
621
- return;
2643
+ catch (error) {
2644
+ return false;
2645
+ }
2646
+ }
2647
+
2648
+ /**
2649
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
2650
+ *
2651
+ * @public exported from `@promptbook/core`
2652
+ */
2653
+ class ParseError extends Error {
2654
+ constructor(message) {
2655
+ super(message);
2656
+ this.name = 'ParseError';
2657
+ Object.setPrototypeOf(this, ParseError.prototype);
622
2658
  }
623
- // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
624
- throw new WrappedError(whatWasThrown);
625
2659
  }
2660
+ /**
2661
+ * TODO: Maybe split `ParseError` and `ApplyError`
2662
+ */
626
2663
 
627
2664
  /**
628
2665
  * Function isValidJsonString will tell you if the string is valid JSON or not
@@ -873,33 +2910,6 @@
873
2910
  return orderedValue;
874
2911
  }
875
2912
 
876
- /**
877
- * Freezes the given object and all its nested objects recursively
878
- *
879
- * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
880
- * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
881
- *
882
- * @returns The same object as the input, but deeply frozen
883
- * @public exported from `@promptbook/utils`
884
- */
885
- function $deepFreeze(objectValue) {
886
- if (Array.isArray(objectValue)) {
887
- return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
888
- }
889
- const propertyNames = Object.getOwnPropertyNames(objectValue);
890
- for (const propertyName of propertyNames) {
891
- const value = objectValue[propertyName];
892
- if (value && typeof value === 'object') {
893
- $deepFreeze(value);
894
- }
895
- }
896
- Object.freeze(objectValue);
897
- return objectValue;
898
- }
899
- /**
900
- * TODO: [🧠] Is there a way how to meaningfully test this utility
901
- */
902
-
903
2913
  /**
904
2914
  * Checks if the value is [🚉] serializable as JSON
905
2915
  * If not, throws an UnexpectedError with a rich error message and tracking
@@ -2121,19 +4131,6 @@
2121
4131
  * TODO: [🧠][🌂] Add id to all errors
2122
4132
  */
2123
4133
 
2124
- /**
2125
- * Error thrown when a fetch request fails
2126
- *
2127
- * @public exported from `@promptbook/core`
2128
- */
2129
- class PromptbookFetchError extends Error {
2130
- constructor(message) {
2131
- super(message);
2132
- this.name = 'PromptbookFetchError';
2133
- Object.setPrototypeOf(this, PromptbookFetchError.prototype);
2134
- }
2135
- }
2136
-
2137
4134
  /**
2138
4135
  * Index of all custom errors
2139
4136
  *
@@ -4112,7 +6109,7 @@
4112
6109
  */
4113
6110
  async function executeAttempts(options) {
4114
6111
  const { jokerParameterNames, priority, maxAttempts, // <- Note: [💂]
4115
- preparedContent, parameters, task, preparedPipeline, tools, $executionReport, pipelineIdentification, maxExecutionAttempts, } = options;
6112
+ preparedContent, parameters, task, preparedPipeline, tools, $executionReport, pipelineIdentification, maxExecutionAttempts, onProgress, } = options;
4116
6113
  const $ongoingTaskResult = {
4117
6114
  $result: null,
4118
6115
  $resultString: null,
@@ -4356,6 +6353,10 @@
4356
6353
  result: $ongoingTaskResult.$resultString,
4357
6354
  error: error,
4358
6355
  });
6356
+ // Report failed attempt
6357
+ onProgress({
6358
+ errors: [error],
6359
+ });
4359
6360
  }
4360
6361
  finally {
4361
6362
  if (!isJokerAttempt &&
@@ -5835,37 +7836,6 @@
5835
7836
  return value;
5836
7837
  }
5837
7838
 
5838
- /**
5839
- * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
5840
- *
5841
- * @public exported from `@promptbook/core`
5842
- */
5843
- const promptbookFetch = async (urlOrRequest, init) => {
5844
- try {
5845
- return await fetch(urlOrRequest, init);
5846
- }
5847
- catch (error) {
5848
- assertsError(error);
5849
- let url;
5850
- if (typeof urlOrRequest === 'string') {
5851
- url = urlOrRequest;
5852
- }
5853
- else if (urlOrRequest instanceof Request) {
5854
- url = urlOrRequest.url;
5855
- }
5856
- throw new PromptbookFetchError(spaceTrim__default["default"]((block) => `
5857
- Can not fetch "${url}"
5858
-
5859
- Fetch error:
5860
- ${block(error.message)}
5861
-
5862
- `));
5863
- }
5864
- };
5865
- /**
5866
- * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
5867
- */
5868
-
5869
7839
  /**
5870
7840
  * Factory function that creates a handler for processing knowledge sources.
5871
7841
  * Provides standardized processing of different types of knowledge sources
@@ -5920,7 +7890,23 @@
5920
7890
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5921
7891
  const rootDirname = path.join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
5922
7892
  const filepath = path.join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
5923
- await tools.fs.mkdir(path.dirname(path.join(rootDirname, filepath)), { recursive: true });
7893
+ // Note: Try to create cache directory, but don't fail if filesystem has issues
7894
+ try {
7895
+ await tools.fs.mkdir(path.dirname(path.join(rootDirname, filepath)), { recursive: true });
7896
+ }
7897
+ catch (error) {
7898
+ // Note: If we can't create cache directory, we'll handle it when trying to write the file
7899
+ // This handles read-only filesystems, permission issues, and missing parent directories
7900
+ if (error instanceof Error && (error.message.includes('EROFS') ||
7901
+ error.message.includes('read-only') ||
7902
+ error.message.includes('EACCES') ||
7903
+ error.message.includes('EPERM') ||
7904
+ error.message.includes('ENOENT'))) ;
7905
+ else {
7906
+ // Re-throw other unexpected errors
7907
+ throw error;
7908
+ }
7909
+ }
5924
7910
  const fileContent = Buffer.from(await response.arrayBuffer());
5925
7911
  if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
5926
7912
  throw new LimitReachedError(`File is too large (${Math.round(fileContent.length / 1024 / 1024)}MB). Maximum allowed size is ${Math.round(DEFAULT_MAX_FILE_SIZE / 1024 / 1024)}MB.`);
@@ -5935,7 +7921,8 @@
5935
7921
  if (error instanceof Error && (error.message.includes('EROFS') ||
5936
7922
  error.message.includes('read-only') ||
5937
7923
  error.message.includes('EACCES') ||
5938
- error.message.includes('EPERM'))) {
7924
+ error.message.includes('EPERM') ||
7925
+ error.message.includes('ENOENT'))) {
5939
7926
  // Return a handler that works directly with the downloaded content
5940
7927
  return {
5941
7928
  source: name,
@@ -11632,18 +13619,28 @@
11632
13619
  * @public exported from `@promptbook/core`
11633
13620
  */
11634
13621
  function book(strings, ...values) {
11635
- const pipelineString = prompt(strings, ...values);
11636
- if (!isValidPipelineString(pipelineString)) {
13622
+ const bookString = prompt(strings, ...values);
13623
+ if (!isValidPipelineString(bookString)) {
11637
13624
  // TODO: Make the CustomError for this
11638
13625
  throw new Error(spaceTrim__default["default"](`
11639
13626
  The string is not a valid pipeline string
11640
13627
 
11641
13628
  book\`
11642
- ${pipelineString}
13629
+ ${bookString}
11643
13630
  \`
11644
13631
  `));
11645
13632
  }
11646
- return pipelineString;
13633
+ if (!isValidBook(bookString)) {
13634
+ // TODO: Make the CustomError for this
13635
+ throw new Error(spaceTrim__default["default"](`
13636
+ The string is not a valid book
13637
+
13638
+ book\`
13639
+ ${bookString}
13640
+ \`
13641
+ `));
13642
+ }
13643
+ return bookString;
11647
13644
  }
11648
13645
  /**
11649
13646
  * TODO: [🧠][🈴] Where is the best location for this file
@@ -11994,6 +13991,7 @@
11994
13991
  exports.CompletionFormfactorDefinition = CompletionFormfactorDefinition;
11995
13992
  exports.CsvFormatError = CsvFormatError;
11996
13993
  exports.CsvFormatParser = CsvFormatParser;
13994
+ exports.DEFAULT_BOOK = DEFAULT_BOOK;
11997
13995
  exports.DEFAULT_BOOKS_DIRNAME = DEFAULT_BOOKS_DIRNAME;
11998
13996
  exports.DEFAULT_BOOK_OUTPUT_PARAMETER_NAME = DEFAULT_BOOK_OUTPUT_PARAMETER_NAME;
11999
13997
  exports.DEFAULT_BOOK_TITLE = DEFAULT_BOOK_TITLE;
@@ -12038,6 +14036,7 @@
12038
14036
  exports.NAME = NAME;
12039
14037
  exports.NonTaskSectionTypes = NonTaskSectionTypes;
12040
14038
  exports.NotFoundError = NotFoundError;
14039
+ exports.NotYetImplementedCommitmentDefinition = NotYetImplementedCommitmentDefinition;
12041
14040
  exports.NotYetImplementedError = NotYetImplementedError;
12042
14041
  exports.ORDER_OF_PIPELINE_JSON = ORDER_OF_PIPELINE_JSON;
12043
14042
  exports.PENDING_VALUE_PLACEHOLDER = PENDING_VALUE_PLACEHOLDER;
@@ -12086,9 +14085,12 @@
12086
14085
  exports.compilePipeline = compilePipeline;
12087
14086
  exports.computeCosineSimilarity = computeCosineSimilarity;
12088
14087
  exports.countUsage = countUsage;
14088
+ exports.createAgentModelRequirements = createAgentModelRequirements;
14089
+ exports.createBasicAgentModelRequirements = createBasicAgentModelRequirements;
12089
14090
  exports.createCollectionFromJson = createCollectionFromJson;
12090
14091
  exports.createCollectionFromPromise = createCollectionFromPromise;
12091
14092
  exports.createCollectionFromUrl = createCollectionFromUrl;
14093
+ exports.createEmptyAgentModelRequirements = createEmptyAgentModelRequirements;
12092
14094
  exports.createLlmToolsFromConfiguration = createLlmToolsFromConfiguration;
12093
14095
  exports.createPipelineExecutor = createPipelineExecutor;
12094
14096
  exports.createSubcollection = createSubcollection;
@@ -12096,17 +14098,23 @@
12096
14098
  exports.executionReportJsonToString = executionReportJsonToString;
12097
14099
  exports.extractParameterNamesFromTask = extractParameterNamesFromTask;
12098
14100
  exports.filterModels = filterModels;
14101
+ exports.getAllCommitmentDefinitions = getAllCommitmentDefinitions;
14102
+ exports.getAllCommitmentTypes = getAllCommitmentTypes;
14103
+ exports.getCommitmentDefinition = getCommitmentDefinition;
12099
14104
  exports.getPipelineInterface = getPipelineInterface;
12100
14105
  exports.identificationToPromptbookToken = identificationToPromptbookToken;
14106
+ exports.isCommitmentSupported = isCommitmentSupported;
12101
14107
  exports.isPassingExpectations = isPassingExpectations;
12102
14108
  exports.isPipelineImplementingInterface = isPipelineImplementingInterface;
12103
14109
  exports.isPipelineInterfacesEqual = isPipelineInterfacesEqual;
12104
14110
  exports.isPipelinePrepared = isPipelinePrepared;
14111
+ exports.isValidBook = isValidBook;
12105
14112
  exports.isValidPipelineString = isValidPipelineString;
12106
14113
  exports.joinLlmExecutionTools = joinLlmExecutionTools;
12107
14114
  exports.limitTotalUsage = limitTotalUsage;
12108
14115
  exports.makeKnowledgeSourceHandler = makeKnowledgeSourceHandler;
12109
14116
  exports.migratePipeline = migratePipeline;
14117
+ exports.parseAgentSource = parseAgentSource;
12110
14118
  exports.parsePipeline = parsePipeline;
12111
14119
  exports.pipelineJsonToString = pipelineJsonToString;
12112
14120
  exports.prepareKnowledgePieces = prepareKnowledgePieces;
@@ -12118,6 +14126,7 @@
12118
14126
  exports.unpreparePipeline = unpreparePipeline;
12119
14127
  exports.usageToHuman = usageToHuman;
12120
14128
  exports.usageToWorktime = usageToWorktime;
14129
+ exports.validateBook = validateBook;
12121
14130
  exports.validatePipeline = validatePipeline;
12122
14131
  exports.validatePipelineString = validatePipelineString;
12123
14132