@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/esm/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
1
+ import spaceTrim$1, { spaceTrim } from 'spacetrim';
2
2
  import { format } from 'prettier';
3
3
  import parserHtml from 'prettier/parser-html';
4
4
  import { randomBytes } from 'crypto';
@@ -27,136 +27,342 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
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(`
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(`
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
@@ -516,7 +722,7 @@ true);
516
722
  function getErrorReportUrl(error) {
517
723
  const report = {
518
724
  title: `🐜 Error report from ${NAME}`,
519
- body: spaceTrim((block) => `
725
+ body: spaceTrim$1((block) => `
520
726
 
521
727
 
522
728
  \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
@@ -559,7 +765,7 @@ function getErrorReportUrl(error) {
559
765
  */
560
766
  class UnexpectedError extends Error {
561
767
  constructor(message) {
562
- super(spaceTrim$1((block) => `
768
+ super(spaceTrim((block) => `
563
769
  ${block(message)}
564
770
 
565
771
  Note: This error should not happen.
@@ -577,52 +783,1883 @@ class UnexpectedError extends Error {
577
783
  }
578
784
 
579
785
  /**
580
- * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
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(`
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$1((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(`
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(`
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(`
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(`
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(`
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(`
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(`
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(`
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(`
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(`
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(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(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$1(`
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
2543
+ *
2544
+ * @public exported from `@promptbook/core`
2545
+ */
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
+ */
2554
+
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;
2566
+ }
2567
+ return /^.+@.+\..+$/.test(email);
2568
+ }
2569
+
2570
+ /**
2571
+ * Tests if given string is valid URL.
2572
+ *
2573
+ * Note: This does not check if the file exists only if the path is valid
2574
+ * @public exported from `@promptbook/utils`
2575
+ */
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;
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;
2614
+ }
2615
+ /**
2616
+ * TODO: [🍏] Implement for MacOs
2617
+ */
2618
+
2619
+ /**
2620
+ * Tests if given string is valid URL.
2621
+ *
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
2626
+ *
2627
+ * @public exported from `@promptbook/utils`
2628
+ */
2629
+ function isValidUrl(url) {
2630
+ if (typeof url !== 'string') {
2631
+ return false;
2632
+ }
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;
2642
+ }
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
581
2650
  *
582
2651
  * @public exported from `@promptbook/core`
583
2652
  */
584
- class WrappedError extends Error {
585
- constructor(whatWasThrown) {
586
- const tag = `[🤮]`;
587
- console.error(tag, whatWasThrown);
588
- super(spaceTrim$1(`
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);
2653
+ class ParseError extends Error {
2654
+ constructor(message) {
2655
+ super(message);
2656
+ this.name = 'ParseError';
2657
+ Object.setPrototypeOf(this, ParseError.prototype);
596
2658
  }
597
2659
  }
598
-
599
2660
  /**
600
- * Helper used in catch blocks to assert that the error is an instance of `Error`
601
- *
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
605
- *
606
- * @private within the repository
2661
+ * TODO: Maybe split `ParseError` and `ApplyError`
607
2662
  */
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;
613
- }
614
- // Case 2: Handle unexpected errors
615
- if (whatWasThrown instanceof UnexpectedError) {
616
- const unexpectedError = whatWasThrown;
617
- throw unexpectedError;
618
- }
619
- // Case 3: Handle standard errors - keep them up to consumer
620
- if (whatWasThrown instanceof Error) {
621
- return;
622
- }
623
- // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
624
- throw new WrappedError(whatWasThrown);
625
- }
626
2663
 
627
2664
  /**
628
2665
  * Function isValidJsonString will tell you if the string is valid JSON or not
@@ -829,7 +2866,7 @@ function pipelineJsonToString(pipelineJson) {
829
2866
  pipelineString += '\n\n';
830
2867
  pipelineString += '```' + contentLanguage;
831
2868
  pipelineString += '\n';
832
- pipelineString += spaceTrim(content);
2869
+ pipelineString += spaceTrim$1(content);
833
2870
  // <- TODO: [main] !!3 Escape
834
2871
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
835
2872
  pipelineString += '\n';
@@ -873,33 +2910,6 @@ function orderJson(options) {
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
@@ -950,7 +2960,7 @@ function checkSerializableAsJson(options) {
950
2960
  }
951
2961
  else if (typeof value === 'object') {
952
2962
  if (value instanceof Date) {
953
- throw new UnexpectedError(spaceTrim((block) => `
2963
+ throw new UnexpectedError(spaceTrim$1((block) => `
954
2964
  \`${name}\` is Date
955
2965
 
956
2966
  Use \`string_date_iso8601\` instead
@@ -969,7 +2979,7 @@ function checkSerializableAsJson(options) {
969
2979
  throw new UnexpectedError(`${name} is RegExp`);
970
2980
  }
971
2981
  else if (value instanceof Error) {
972
- throw new UnexpectedError(spaceTrim((block) => `
2982
+ throw new UnexpectedError(spaceTrim$1((block) => `
973
2983
  \`${name}\` is unserialized Error
974
2984
 
975
2985
  Use function \`serializeError\`
@@ -992,7 +3002,7 @@ function checkSerializableAsJson(options) {
992
3002
  }
993
3003
  catch (error) {
994
3004
  assertsError(error);
995
- throw new UnexpectedError(spaceTrim((block) => `
3005
+ throw new UnexpectedError(spaceTrim$1((block) => `
996
3006
  \`${name}\` is not serializable
997
3007
 
998
3008
  ${block(error.stack || error.message)}
@@ -1024,7 +3034,7 @@ function checkSerializableAsJson(options) {
1024
3034
  }
1025
3035
  }
1026
3036
  else {
1027
- throw new UnexpectedError(spaceTrim((block) => `
3037
+ throw new UnexpectedError(spaceTrim$1((block) => `
1028
3038
  \`${name}\` is unknown type
1029
3039
 
1030
3040
  Additional message for \`${name}\`:
@@ -1311,7 +3321,7 @@ function validatePipeline(pipeline) {
1311
3321
  if (!(error instanceof PipelineLogicError)) {
1312
3322
  throw error;
1313
3323
  }
1314
- console.error(spaceTrim$1((block) => `
3324
+ console.error(spaceTrim((block) => `
1315
3325
  Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
1316
3326
 
1317
3327
  ${block(error.message)}
@@ -1338,7 +3348,7 @@ function validatePipeline_InnerFunction(pipeline) {
1338
3348
  })();
1339
3349
  if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
1340
3350
  // <- Note: [🚲]
1341
- throw new PipelineLogicError(spaceTrim$1((block) => `
3351
+ throw new PipelineLogicError(spaceTrim((block) => `
1342
3352
  Invalid promptbook URL "${pipeline.pipelineUrl}"
1343
3353
 
1344
3354
  ${block(pipelineIdentification)}
@@ -1346,7 +3356,7 @@ function validatePipeline_InnerFunction(pipeline) {
1346
3356
  }
1347
3357
  if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
1348
3358
  // <- Note: [🚲]
1349
- throw new PipelineLogicError(spaceTrim$1((block) => `
3359
+ throw new PipelineLogicError(spaceTrim((block) => `
1350
3360
  Invalid Promptbook Version "${pipeline.bookVersion}"
1351
3361
 
1352
3362
  ${block(pipelineIdentification)}
@@ -1355,7 +3365,7 @@ function validatePipeline_InnerFunction(pipeline) {
1355
3365
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
1356
3366
  if (!Array.isArray(pipeline.parameters)) {
1357
3367
  // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1358
- throw new ParseError(spaceTrim$1((block) => `
3368
+ throw new ParseError(spaceTrim((block) => `
1359
3369
  Pipeline is valid JSON but with wrong structure
1360
3370
 
1361
3371
  \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
@@ -1366,7 +3376,7 @@ function validatePipeline_InnerFunction(pipeline) {
1366
3376
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
1367
3377
  if (!Array.isArray(pipeline.tasks)) {
1368
3378
  // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1369
- throw new ParseError(spaceTrim$1((block) => `
3379
+ throw new ParseError(spaceTrim((block) => `
1370
3380
  Pipeline is valid JSON but with wrong structure
1371
3381
 
1372
3382
  \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
@@ -1392,7 +3402,7 @@ function validatePipeline_InnerFunction(pipeline) {
1392
3402
  // Note: Check each parameter individually
1393
3403
  for (const parameter of pipeline.parameters) {
1394
3404
  if (parameter.isInput && parameter.isOutput) {
1395
- throw new PipelineLogicError(spaceTrim$1((block) => `
3405
+ throw new PipelineLogicError(spaceTrim((block) => `
1396
3406
 
1397
3407
  Parameter \`{${parameter.name}}\` can not be both input and output
1398
3408
 
@@ -1403,7 +3413,7 @@ function validatePipeline_InnerFunction(pipeline) {
1403
3413
  if (!parameter.isInput &&
1404
3414
  !parameter.isOutput &&
1405
3415
  !pipeline.tasks.some((task) => task.dependentParameterNames.includes(parameter.name))) {
1406
- throw new PipelineLogicError(spaceTrim$1((block) => `
3416
+ throw new PipelineLogicError(spaceTrim((block) => `
1407
3417
  Parameter \`{${parameter.name}}\` is created but not used
1408
3418
 
1409
3419
  You can declare {${parameter.name}} as output parameter by adding in the header:
@@ -1415,7 +3425,7 @@ function validatePipeline_InnerFunction(pipeline) {
1415
3425
  }
1416
3426
  // Note: Testing that parameter is either input or result of some task
1417
3427
  if (!parameter.isInput && !pipeline.tasks.some((task) => task.resultingParameterName === parameter.name)) {
1418
- throw new PipelineLogicError(spaceTrim$1((block) => `
3428
+ throw new PipelineLogicError(spaceTrim((block) => `
1419
3429
  Parameter \`{${parameter.name}}\` is declared but not defined
1420
3430
 
1421
3431
  You can do one of these:
@@ -1431,14 +3441,14 @@ function validatePipeline_InnerFunction(pipeline) {
1431
3441
  // Note: Checking each task individually
1432
3442
  for (const task of pipeline.tasks) {
1433
3443
  if (definedParameters.has(task.resultingParameterName)) {
1434
- throw new PipelineLogicError(spaceTrim$1((block) => `
3444
+ throw new PipelineLogicError(spaceTrim((block) => `
1435
3445
  Parameter \`{${task.resultingParameterName}}\` is defined multiple times
1436
3446
 
1437
3447
  ${block(pipelineIdentification)}
1438
3448
  `));
1439
3449
  }
1440
3450
  if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
1441
- throw new PipelineLogicError(spaceTrim$1((block) => `
3451
+ throw new PipelineLogicError(spaceTrim((block) => `
1442
3452
  Parameter name {${task.resultingParameterName}} is reserved, please use different name
1443
3453
 
1444
3454
  ${block(pipelineIdentification)}
@@ -1448,7 +3458,7 @@ function validatePipeline_InnerFunction(pipeline) {
1448
3458
  if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
1449
3459
  if (!task.format &&
1450
3460
  !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
1451
- throw new PipelineLogicError(spaceTrim$1((block) => `
3461
+ throw new PipelineLogicError(spaceTrim((block) => `
1452
3462
  Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
1453
3463
 
1454
3464
  ${block(pipelineIdentification)}
@@ -1456,7 +3466,7 @@ function validatePipeline_InnerFunction(pipeline) {
1456
3466
  }
1457
3467
  for (const joker of task.jokerParameterNames) {
1458
3468
  if (!task.dependentParameterNames.includes(joker)) {
1459
- throw new PipelineLogicError(spaceTrim$1((block) => `
3469
+ throw new PipelineLogicError(spaceTrim((block) => `
1460
3470
  Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
1461
3471
 
1462
3472
  ${block(pipelineIdentification)}
@@ -1467,21 +3477,21 @@ function validatePipeline_InnerFunction(pipeline) {
1467
3477
  if (task.expectations) {
1468
3478
  for (const [unit, { min, max }] of Object.entries(task.expectations)) {
1469
3479
  if (min !== undefined && max !== undefined && min > max) {
1470
- throw new PipelineLogicError(spaceTrim$1((block) => `
3480
+ throw new PipelineLogicError(spaceTrim((block) => `
1471
3481
  Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
1472
3482
 
1473
3483
  ${block(pipelineIdentification)}
1474
3484
  `));
1475
3485
  }
1476
3486
  if (min !== undefined && min < 0) {
1477
- throw new PipelineLogicError(spaceTrim$1((block) => `
3487
+ throw new PipelineLogicError(spaceTrim((block) => `
1478
3488
  Min expectation of ${unit} must be zero or positive
1479
3489
 
1480
3490
  ${block(pipelineIdentification)}
1481
3491
  `));
1482
3492
  }
1483
3493
  if (max !== undefined && max <= 0) {
1484
- throw new PipelineLogicError(spaceTrim$1((block) => `
3494
+ throw new PipelineLogicError(spaceTrim((block) => `
1485
3495
  Max expectation of ${unit} must be positive
1486
3496
 
1487
3497
  ${block(pipelineIdentification)}
@@ -1503,7 +3513,7 @@ function validatePipeline_InnerFunction(pipeline) {
1503
3513
  while (unresovedTasks.length > 0) {
1504
3514
  if (loopLimit-- < 0) {
1505
3515
  // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
1506
- throw new UnexpectedError(spaceTrim$1((block) => `
3516
+ throw new UnexpectedError(spaceTrim((block) => `
1507
3517
  Loop limit reached during detection of circular dependencies in \`validatePipeline\`
1508
3518
 
1509
3519
  ${block(pipelineIdentification)}
@@ -1513,7 +3523,7 @@ function validatePipeline_InnerFunction(pipeline) {
1513
3523
  if (currentlyResovedTasks.length === 0) {
1514
3524
  throw new PipelineLogicError(
1515
3525
  // TODO: [🐎] DRY
1516
- spaceTrim$1((block) => `
3526
+ spaceTrim((block) => `
1517
3527
 
1518
3528
  Can not resolve some parameters:
1519
3529
  Either you are using a parameter that is not defined, or there are some circular dependencies.
@@ -1677,7 +3687,7 @@ class SimplePipelineCollection {
1677
3687
  for (const pipeline of pipelines) {
1678
3688
  // TODO: [👠] DRY
1679
3689
  if (pipeline.pipelineUrl === undefined) {
1680
- throw new PipelineUrlError(spaceTrim$1(`
3690
+ throw new PipelineUrlError(spaceTrim(`
1681
3691
  Pipeline with name "${pipeline.title}" does not have defined URL
1682
3692
 
1683
3693
  File:
@@ -1699,7 +3709,7 @@ class SimplePipelineCollection {
1699
3709
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
1700
3710
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
1701
3711
  const existing = this.collection.get(pipeline.pipelineUrl);
1702
- throw new PipelineUrlError(spaceTrim$1(`
3712
+ throw new PipelineUrlError(spaceTrim(`
1703
3713
  Pipeline with URL ${pipeline.pipelineUrl} is already in the collection 🍎
1704
3714
 
1705
3715
  Conflicting files:
@@ -1731,13 +3741,13 @@ class SimplePipelineCollection {
1731
3741
  const pipeline = this.collection.get(url);
1732
3742
  if (!pipeline) {
1733
3743
  if (this.listPipelines().length === 0) {
1734
- throw new NotFoundError(spaceTrim$1(`
3744
+ throw new NotFoundError(spaceTrim(`
1735
3745
  Pipeline with url "${url}" not found
1736
3746
 
1737
3747
  No pipelines available
1738
3748
  `));
1739
3749
  }
1740
- throw new NotFoundError(spaceTrim$1((block) => `
3750
+ throw new NotFoundError(spaceTrim((block) => `
1741
3751
  Pipeline with url "${url}" not found
1742
3752
 
1743
3753
  Available pipelines:
@@ -1867,7 +3877,7 @@ function createSubcollection(collection, predicate) {
1867
3877
  }
1868
3878
  async function getPipelineByUrl(url) {
1869
3879
  if (!predicate(url)) {
1870
- throw new NotFoundError(await spaceTrim$1(async (block) => `
3880
+ throw new NotFoundError(await spaceTrim(async (block) => `
1871
3881
  Promptbook with url "${url}" not found or not accessible
1872
3882
 
1873
3883
  Available promptbooks:
@@ -1902,7 +3912,7 @@ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"
1902
3912
  */
1903
3913
  class MissingToolsError extends Error {
1904
3914
  constructor(message) {
1905
- super(spaceTrim$1((block) => `
3915
+ super(spaceTrim((block) => `
1906
3916
  ${block(message)}
1907
3917
 
1908
3918
  Note: You have probably forgot to provide some tools for pipeline execution or preparation
@@ -2071,7 +4081,7 @@ class LimitReachedError extends Error {
2071
4081
  */
2072
4082
  class NotYetImplementedError extends Error {
2073
4083
  constructor(message) {
2074
- super(spaceTrim$1((block) => `
4084
+ super(spaceTrim((block) => `
2075
4085
  ${block(message)}
2076
4086
 
2077
4087
  Note: This feature is not implemented yet but it will be soon.
@@ -2121,19 +4131,6 @@ class PipelineExecutionError extends Error {
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
  *
@@ -2205,7 +4202,7 @@ function serializeError(error) {
2205
4202
  const { name, message, stack } = error;
2206
4203
  const { id } = error;
2207
4204
  if (!Object.keys(ALL_ERRORS).includes(name)) {
2208
- console.error(spaceTrim((block) => `
4205
+ console.error(spaceTrim$1((block) => `
2209
4206
 
2210
4207
  Cannot serialize error with name "${name}"
2211
4208
 
@@ -2238,7 +4235,7 @@ function jsonParse(value) {
2238
4235
  }
2239
4236
  else if (typeof value !== 'string') {
2240
4237
  console.error('Can not parse JSON from non-string value.', { text: value });
2241
- throw new Error(spaceTrim(`
4238
+ throw new Error(spaceTrim$1(`
2242
4239
  Can not parse JSON from non-string value.
2243
4240
 
2244
4241
  The value type: ${typeof value}
@@ -2252,7 +4249,7 @@ function jsonParse(value) {
2252
4249
  if (!(error instanceof Error)) {
2253
4250
  throw error;
2254
4251
  }
2255
- throw new Error(spaceTrim((block) => `
4252
+ throw new Error(spaceTrim$1((block) => `
2256
4253
  ${block(error.message)}
2257
4254
 
2258
4255
  The expected JSON text:
@@ -2305,7 +4302,7 @@ function deserializeError(error) {
2305
4302
  message = `${name}: ${message}`;
2306
4303
  }
2307
4304
  if (stack !== undefined && stack !== '') {
2308
- message = spaceTrim((block) => `
4305
+ message = spaceTrim$1((block) => `
2309
4306
  ${block(message)}
2310
4307
 
2311
4308
  Original stack trace:
@@ -2342,11 +4339,11 @@ function assertsTaskSuccessful(executionResult) {
2342
4339
  throw deserializeError(errors[0]);
2343
4340
  }
2344
4341
  else {
2345
- throw new PipelineExecutionError(spaceTrim$1((block) => `
4342
+ throw new PipelineExecutionError(spaceTrim((block) => `
2346
4343
  Multiple errors occurred during Promptbook execution
2347
4344
 
2348
4345
  ${block(errors
2349
- .map(({ name, stack, message }, index) => spaceTrim$1((block) => `
4346
+ .map(({ name, stack, message }, index) => spaceTrim((block) => `
2350
4347
  ${name} ${index + 1}:
2351
4348
  ${block(stack || message)}
2352
4349
  `))
@@ -2716,7 +4713,7 @@ function extractVariablesFromJavascript(script) {
2716
4713
  }
2717
4714
  catch (error) {
2718
4715
  assertsError(error);
2719
- throw new ParseError(spaceTrim$1((block) => `
4716
+ throw new ParseError(spaceTrim((block) => `
2720
4717
  Can not extract variables from the script
2721
4718
  ${block(error.stack || error.message)}
2722
4719
 
@@ -2899,7 +4896,7 @@ const CsvFormatParser = {
2899
4896
  const { value, outputParameterName, settings, mapCallback, onProgress } = options;
2900
4897
  const csv = csvParse(value, settings);
2901
4898
  if (csv.errors.length !== 0) {
2902
- throw new CsvFormatError(spaceTrim((block) => `
4899
+ throw new CsvFormatError(spaceTrim$1((block) => `
2903
4900
  CSV parsing error
2904
4901
 
2905
4902
  Error(s) from CSV parsing:
@@ -2944,7 +4941,7 @@ const CsvFormatParser = {
2944
4941
  const { value, settings, mapCallback, onProgress } = options;
2945
4942
  const csv = csvParse(value, settings);
2946
4943
  if (csv.errors.length !== 0) {
2947
- throw new CsvFormatError(spaceTrim((block) => `
4944
+ throw new CsvFormatError(spaceTrim$1((block) => `
2948
4945
  CSV parsing error
2949
4946
 
2950
4947
  Error(s) from CSV parsing:
@@ -3154,7 +5151,7 @@ function mapAvailableToExpectedParameters(options) {
3154
5151
  }
3155
5152
  // Phase 2️⃣: Non-matching mapping
3156
5153
  if (expectedParameterNames.size !== availableParametersNames.size) {
3157
- throw new PipelineExecutionError(spaceTrim((block) => `
5154
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
3158
5155
  Can not map available parameters to expected parameters
3159
5156
 
3160
5157
  Mapped parameters:
@@ -3207,14 +5204,14 @@ class MultipleLlmExecutionTools {
3207
5204
  if (description === undefined) {
3208
5205
  return headLine;
3209
5206
  }
3210
- return spaceTrim((block) => `
5207
+ return spaceTrim$1((block) => `
3211
5208
  ${headLine}
3212
5209
 
3213
5210
  ${ /* <- Note: Indenting the description: */block(description)}
3214
5211
  `);
3215
5212
  })
3216
5213
  .join('\n\n');
3217
- return spaceTrim((block) => `
5214
+ return spaceTrim$1((block) => `
3218
5215
  Multiple LLM Providers:
3219
5216
 
3220
5217
  ${block(innerModelsTitlesAndDescriptions)}
@@ -3305,7 +5302,7 @@ class MultipleLlmExecutionTools {
3305
5302
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
3306
5303
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
3307
5304
  // 3) ...
3308
- spaceTrim((block) => `
5305
+ spaceTrim$1((block) => `
3309
5306
  All execution tools failed:
3310
5307
 
3311
5308
  ${block(errors
@@ -3318,7 +5315,7 @@ class MultipleLlmExecutionTools {
3318
5315
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\``);
3319
5316
  }
3320
5317
  else {
3321
- throw new PipelineExecutionError(spaceTrim((block) => `
5318
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
3322
5319
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}"
3323
5320
 
3324
5321
  Available \`LlmExecutionTools\`:
@@ -3351,7 +5348,7 @@ class MultipleLlmExecutionTools {
3351
5348
  */
3352
5349
  function joinLlmExecutionTools(...llmExecutionTools) {
3353
5350
  if (llmExecutionTools.length === 0) {
3354
- const warningMessage = spaceTrim(`
5351
+ const warningMessage = spaceTrim$1(`
3355
5352
  You have not provided any \`LlmExecutionTools\`
3356
5353
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
3357
5354
 
@@ -4063,7 +6060,7 @@ function validatePromptResult(options) {
4063
6060
  }
4064
6061
  catch (error) {
4065
6062
  keepUnused(error);
4066
- throw new ExpectError(spaceTrim$1((block) => `
6063
+ throw new ExpectError(spaceTrim((block) => `
4067
6064
  Expected valid JSON string
4068
6065
 
4069
6066
  The expected JSON text:
@@ -4112,7 +6109,7 @@ function validatePromptResult(options) {
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,
@@ -4128,7 +6125,7 @@ async function executeAttempts(options) {
4128
6125
  const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
4129
6126
  // TODO: [🧠][🍭] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
4130
6127
  if (isJokerAttempt && !jokerParameterName) {
4131
- throw new UnexpectedError(spaceTrim$1((block) => `
6128
+ throw new UnexpectedError(spaceTrim((block) => `
4132
6129
  Joker not found in attempt ${attemptIndex}
4133
6130
 
4134
6131
  ${block(pipelineIdentification)}
@@ -4139,7 +6136,7 @@ async function executeAttempts(options) {
4139
6136
  $ongoingTaskResult.$expectError = null;
4140
6137
  if (isJokerAttempt) {
4141
6138
  if (parameters[jokerParameterName] === undefined) {
4142
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6139
+ throw new PipelineExecutionError(spaceTrim((block) => `
4143
6140
  Joker parameter {${jokerParameterName}} not defined
4144
6141
 
4145
6142
  ${block(pipelineIdentification)}
@@ -4197,7 +6194,7 @@ async function executeAttempts(options) {
4197
6194
  $ongoingTaskResult.$resultString = $ongoingTaskResult.$completionResult.content;
4198
6195
  break variant;
4199
6196
  case 'EMBEDDING':
4200
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6197
+ throw new PipelineExecutionError(spaceTrim((block) => `
4201
6198
  Embedding model can not be used in pipeline
4202
6199
 
4203
6200
  This should be catched during parsing
@@ -4208,7 +6205,7 @@ async function executeAttempts(options) {
4208
6205
  break variant;
4209
6206
  // <- case [🤖]:
4210
6207
  default:
4211
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6208
+ throw new PipelineExecutionError(spaceTrim((block) => `
4212
6209
  Unknown model variant "${task.modelRequirements.modelVariant}"
4213
6210
 
4214
6211
  ${block(pipelineIdentification)}
@@ -4219,14 +6216,14 @@ async function executeAttempts(options) {
4219
6216
  break;
4220
6217
  case 'SCRIPT_TASK':
4221
6218
  if (arrayableToArray(tools.script).length === 0) {
4222
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6219
+ throw new PipelineExecutionError(spaceTrim((block) => `
4223
6220
  No script execution tools are available
4224
6221
 
4225
6222
  ${block(pipelineIdentification)}
4226
6223
  `));
4227
6224
  }
4228
6225
  if (!task.contentLanguage) {
4229
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6226
+ throw new PipelineExecutionError(spaceTrim((block) => `
4230
6227
  Script language is not defined for SCRIPT TASK "${task.name}"
4231
6228
 
4232
6229
  ${block(pipelineIdentification)}
@@ -4257,7 +6254,7 @@ async function executeAttempts(options) {
4257
6254
  throw $ongoingTaskResult.$scriptPipelineExecutionErrors[0];
4258
6255
  }
4259
6256
  else {
4260
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6257
+ throw new PipelineExecutionError(spaceTrim((block) => `
4261
6258
  Script execution failed ${$ongoingTaskResult.$scriptPipelineExecutionErrors.length}x
4262
6259
 
4263
6260
  ${block(pipelineIdentification)}
@@ -4271,7 +6268,7 @@ async function executeAttempts(options) {
4271
6268
  break taskType;
4272
6269
  case 'DIALOG_TASK':
4273
6270
  if (tools.userInterface === undefined) {
4274
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6271
+ throw new PipelineExecutionError(spaceTrim((block) => `
4275
6272
  User interface tools are not available
4276
6273
 
4277
6274
  ${block(pipelineIdentification)}
@@ -4289,7 +6286,7 @@ async function executeAttempts(options) {
4289
6286
  break taskType;
4290
6287
  // <- case: [🅱]
4291
6288
  default:
4292
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6289
+ throw new PipelineExecutionError(spaceTrim((block) => `
4293
6290
  Unknown execution type "${task.taskType}"
4294
6291
 
4295
6292
  ${block(pipelineIdentification)}
@@ -4356,6 +6353,10 @@ async function executeAttempts(options) {
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 &&
@@ -4380,7 +6381,7 @@ async function executeAttempts(options) {
4380
6381
  if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
4381
6382
  // Note: Create a summary of all failures
4382
6383
  const failuresSummary = $ongoingTaskResult.$failedResults
4383
- .map((failure) => spaceTrim$1((block) => {
6384
+ .map((failure) => spaceTrim((block) => {
4384
6385
  var _a, _b;
4385
6386
  return `
4386
6387
  Attempt ${failure.attemptIndex + 1}:
@@ -4390,14 +6391,14 @@ async function executeAttempts(options) {
4390
6391
  Result:
4391
6392
  ${block(failure.result === null
4392
6393
  ? 'null'
4393
- : spaceTrim$1(failure.result)
6394
+ : spaceTrim(failure.result)
4394
6395
  .split('\n')
4395
6396
  .map((line) => `> ${line}`)
4396
6397
  .join('\n'))}
4397
6398
  `;
4398
6399
  }))
4399
6400
  .join('\n\n---\n\n');
4400
- throw new PipelineExecutionError(spaceTrim$1((block) => {
6401
+ throw new PipelineExecutionError(spaceTrim((block) => {
4401
6402
  var _a;
4402
6403
  return `
4403
6404
  LLM execution failed ${maxExecutionAttempts}x
@@ -4417,7 +6418,7 @@ async function executeAttempts(options) {
4417
6418
  }
4418
6419
  }
4419
6420
  if ($ongoingTaskResult.$resultString === null) {
4420
- throw new UnexpectedError(spaceTrim$1((block) => `
6421
+ throw new UnexpectedError(spaceTrim((block) => `
4421
6422
  Something went wrong and prompt result is null
4422
6423
 
4423
6424
  ${block(pipelineIdentification)}
@@ -4444,7 +6445,7 @@ async function executeFormatSubvalues(options) {
4444
6445
  return /* not await */ executeAttempts(options);
4445
6446
  }
4446
6447
  if (jokerParameterNames.length !== 0) {
4447
- throw new UnexpectedError(spaceTrim((block) => `
6448
+ throw new UnexpectedError(spaceTrim$1((block) => `
4448
6449
  JOKER parameters are not supported together with FOREACH command
4449
6450
 
4450
6451
  [🧞‍♀️] This should be prevented in \`validatePipeline\`
@@ -4457,7 +6458,7 @@ async function executeFormatSubvalues(options) {
4457
6458
  if (formatDefinition === undefined) {
4458
6459
  throw new UnexpectedError(
4459
6460
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
4460
- spaceTrim((block) => `
6461
+ spaceTrim$1((block) => `
4461
6462
  Unsupported format "${task.foreach.formatName}"
4462
6463
 
4463
6464
  Available formats:
@@ -4474,7 +6475,7 @@ async function executeFormatSubvalues(options) {
4474
6475
  if (subvalueParser === undefined) {
4475
6476
  throw new UnexpectedError(
4476
6477
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
4477
- spaceTrim((block) => `
6478
+ spaceTrim$1((block) => `
4478
6479
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
4479
6480
 
4480
6481
  Available subformat names for format "${formatDefinition.formatName}":
@@ -4514,7 +6515,7 @@ async function executeFormatSubvalues(options) {
4514
6515
  if (!(error instanceof PipelineExecutionError)) {
4515
6516
  throw error;
4516
6517
  }
4517
- const highLevelError = new PipelineExecutionError(spaceTrim((block) => `
6518
+ const highLevelError = new PipelineExecutionError(spaceTrim$1((block) => `
4518
6519
  ${error.message}
4519
6520
 
4520
6521
  This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -4538,7 +6539,7 @@ async function executeFormatSubvalues(options) {
4538
6539
  ...options,
4539
6540
  priority: priority + index,
4540
6541
  parameters: allSubparameters,
4541
- pipelineIdentification: spaceTrim((block) => `
6542
+ pipelineIdentification: spaceTrim$1((block) => `
4542
6543
  ${block(pipelineIdentification)}
4543
6544
  Subparameter index: ${index}
4544
6545
  `),
@@ -4547,7 +6548,7 @@ async function executeFormatSubvalues(options) {
4547
6548
  }
4548
6549
  catch (error) {
4549
6550
  if (length > BIG_DATASET_TRESHOLD) {
4550
- console.error(spaceTrim((block) => `
6551
+ console.error(spaceTrim$1((block) => `
4551
6552
  ${error.message}
4552
6553
 
4553
6554
  This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -4725,7 +6726,7 @@ async function getReservedParametersForTask(options) {
4725
6726
  // Note: Doublecheck that ALL reserved parameters are defined:
4726
6727
  for (const parameterName of RESERVED_PARAMETER_NAMES) {
4727
6728
  if (reservedParameters[parameterName] === undefined) {
4728
- throw new UnexpectedError(spaceTrim$1((block) => `
6729
+ throw new UnexpectedError(spaceTrim((block) => `
4729
6730
  Reserved parameter {${parameterName}} is not defined
4730
6731
 
4731
6732
  ${block(pipelineIdentification)}
@@ -4751,7 +6752,7 @@ async function executeTask(options) {
4751
6752
  const dependentParameterNames = new Set(currentTask.dependentParameterNames);
4752
6753
  // TODO: [👩🏾‍🤝‍👩🏻] Use here `mapAvailableToExpectedParameters`
4753
6754
  if (difference(union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)), new Set(RESERVED_PARAMETER_NAMES)).size !== 0) {
4754
- throw new UnexpectedError(spaceTrim$1((block) => `
6755
+ throw new UnexpectedError(spaceTrim((block) => `
4755
6756
  Dependent parameters are not consistent with used parameters:
4756
6757
 
4757
6758
  Dependent parameters:
@@ -4795,7 +6796,7 @@ async function executeTask(options) {
4795
6796
  else if (!definedParameterNames.has(parameterName) && usedParameterNames.has(parameterName)) {
4796
6797
  // Houston, we have a problem
4797
6798
  // Note: Checking part is also done in `validatePipeline`, but it’s good to doublecheck
4798
- throw new UnexpectedError(spaceTrim$1((block) => `
6799
+ throw new UnexpectedError(spaceTrim((block) => `
4799
6800
  Parameter \`{${parameterName}}\` is NOT defined
4800
6801
  BUT used in task "${currentTask.title || currentTask.name}"
4801
6802
 
@@ -4863,7 +6864,7 @@ function filterJustOutputParameters(options) {
4863
6864
  for (const parameter of preparedPipeline.parameters.filter(({ isOutput }) => isOutput)) {
4864
6865
  if (parametersToPass[parameter.name] === undefined) {
4865
6866
  // [4]
4866
- $warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
6867
+ $warnings.push(new PipelineExecutionError(spaceTrim((block) => `
4867
6868
  Parameter \`{${parameter.name}}\` should be an output parameter, but it was not generated during pipeline execution
4868
6869
 
4869
6870
  ${block(pipelineIdentification)}
@@ -4948,7 +6949,7 @@ async function executePipeline(options) {
4948
6949
  for (const parameterName of Object.keys(inputParameters)) {
4949
6950
  const parameter = preparedPipeline.parameters.find(({ name }) => name === parameterName);
4950
6951
  if (parameter === undefined) {
4951
- warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
6952
+ warnings.push(new PipelineExecutionError(spaceTrim((block) => `
4952
6953
  Extra parameter {${parameterName}} is being passed which is not part of the pipeline.
4953
6954
 
4954
6955
  ${block(pipelineIdentification)}
@@ -4963,7 +6964,7 @@ async function executePipeline(options) {
4963
6964
  // TODO: [🧠] This should be also non-critical error
4964
6965
  return exportJson({
4965
6966
  name: 'pipelineExecutorResult',
4966
- message: spaceTrim$1((block) => `
6967
+ message: spaceTrim((block) => `
4967
6968
  Unsuccessful PipelineExecutorResult (with extra parameter {${parameter.name}}) PipelineExecutorResult
4968
6969
 
4969
6970
  ${block(pipelineIdentification)}
@@ -4972,7 +6973,7 @@ async function executePipeline(options) {
4972
6973
  value: {
4973
6974
  isSuccessful: false,
4974
6975
  errors: [
4975
- new PipelineExecutionError(spaceTrim$1((block) => `
6976
+ new PipelineExecutionError(spaceTrim((block) => `
4976
6977
  Parameter \`{${parameter.name}}\` is passed as input parameter but it is not input
4977
6978
 
4978
6979
  ${block(pipelineIdentification)}
@@ -4999,7 +7000,7 @@ async function executePipeline(options) {
4999
7000
  while (unresovedTasks.length > 0) {
5000
7001
  if (loopLimit-- < 0) {
5001
7002
  // Note: Really UnexpectedError not LimitReachedError - this should be catched during validatePipeline
5002
- throw new UnexpectedError(spaceTrim$1((block) => `
7003
+ throw new UnexpectedError(spaceTrim((block) => `
5003
7004
  Loop limit reached during resolving parameters pipeline execution
5004
7005
 
5005
7006
  ${block(pipelineIdentification)}
@@ -5009,7 +7010,7 @@ async function executePipeline(options) {
5009
7010
  if (!currentTask && resolving.length === 0) {
5010
7011
  throw new UnexpectedError(
5011
7012
  // TODO: [🐎] DRY
5012
- spaceTrim$1((block) => `
7013
+ spaceTrim((block) => `
5013
7014
  Can not resolve some parameters:
5014
7015
 
5015
7016
  ${block(pipelineIdentification)}
@@ -5049,7 +7050,7 @@ async function executePipeline(options) {
5049
7050
  tools,
5050
7051
  onProgress(newOngoingResult) {
5051
7052
  if (isReturned) {
5052
- throw new UnexpectedError(spaceTrim$1((block) => `
7053
+ throw new UnexpectedError(spaceTrim((block) => `
5053
7054
  Can not call \`onProgress\` after pipeline execution is finished
5054
7055
 
5055
7056
  ${block(pipelineIdentification)}
@@ -5065,7 +7066,7 @@ async function executePipeline(options) {
5065
7066
  }
5066
7067
  },
5067
7068
  $executionReport: executionReport,
5068
- pipelineIdentification: spaceTrim$1((block) => `
7069
+ pipelineIdentification: spaceTrim((block) => `
5069
7070
  ${block(pipelineIdentification)}
5070
7071
  Task name: ${currentTask.name}
5071
7072
  Task title: ${currentTask.title}
@@ -5174,7 +7175,7 @@ function createPipelineExecutor(options) {
5174
7175
  preparedPipeline = pipeline;
5175
7176
  }
5176
7177
  else if (isNotPreparedWarningSuppressed !== true) {
5177
- console.warn(spaceTrim$1((block) => `
7178
+ console.warn(spaceTrim((block) => `
5178
7179
  Pipeline is not prepared
5179
7180
 
5180
7181
  ${block(pipelineIdentification)}
@@ -5198,7 +7199,7 @@ function createPipelineExecutor(options) {
5198
7199
  inputParameters,
5199
7200
  tools,
5200
7201
  onProgress,
5201
- pipelineIdentification: spaceTrim$1((block) => `
7202
+ pipelineIdentification: spaceTrim((block) => `
5202
7203
  ${block(pipelineIdentification)}
5203
7204
  ${runCount === 1 ? '' : `Run #${runCount}`}
5204
7205
  `),
@@ -5609,14 +7610,14 @@ function $registeredScrapersMessage(availableScrapers) {
5609
7610
  return { ...metadata, isMetadataAviailable, isInstalled, isAvailableInTools };
5610
7611
  });
5611
7612
  if (metadata.length === 0) {
5612
- return spaceTrim(`
7613
+ return spaceTrim$1(`
5613
7614
  **No scrapers are available**
5614
7615
 
5615
7616
  This is a unexpected behavior, you are probably using some broken version of Promptbook
5616
7617
  At least there should be available the metadata of the scrapers
5617
7618
  `);
5618
7619
  }
5619
- return spaceTrim((block) => `
7620
+ return spaceTrim$1((block) => `
5620
7621
  Available scrapers are:
5621
7622
  ${block(metadata
5622
7623
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvailableInBrowser, isAvailableInTools, }, i) => {
@@ -5835,37 +7836,6 @@ function titleToName(value) {
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((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 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5920
7890
  // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5921
7891
  const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
5922
7892
  const filepath = join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
5923
- await tools.fs.mkdir(dirname(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(dirname(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 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
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,
@@ -5975,7 +7962,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5975
7962
  const fileExtension = getFileExtension(filename);
5976
7963
  const mimeType = extensionToMimeType(fileExtension || '');
5977
7964
  if (!(await isFileExisting(filename, tools.fs))) {
5978
- throw new NotFoundError(spaceTrim((block) => `
7965
+ throw new NotFoundError(spaceTrim$1((block) => `
5979
7966
  Can not make source handler for file which does not exist:
5980
7967
 
5981
7968
  File:
@@ -6062,7 +8049,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
6062
8049
  // <- TODO: [🪓] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
6063
8050
  break;
6064
8051
  }
6065
- console.warn(spaceTrim((block) => `
8052
+ console.warn(spaceTrim$1((block) => `
6066
8053
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
6067
8054
 
6068
8055
  The source:
@@ -6078,7 +8065,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
6078
8065
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
6079
8066
  }
6080
8067
  if (partialPieces === null) {
6081
- throw new KnowledgeScrapeError(spaceTrim((block) => `
8068
+ throw new KnowledgeScrapeError(spaceTrim$1((block) => `
6082
8069
  Cannot scrape knowledge
6083
8070
 
6084
8071
  The source:
@@ -6157,7 +8144,7 @@ async function prepareTasks(pipeline, tools, options) {
6157
8144
  if (task.taskType === 'PROMPT_TASK' &&
6158
8145
  knowledgePiecesCount > 0 &&
6159
8146
  !dependentParameterNames.includes('knowledge')) {
6160
- preparedContent = spaceTrim$1(`
8147
+ preparedContent = spaceTrim(`
6161
8148
  {content}
6162
8149
 
6163
8150
  ## Knowledge
@@ -6416,7 +8403,7 @@ const knowledgeCommandParser = {
6416
8403
  */
6417
8404
  parse(input) {
6418
8405
  const { args } = input;
6419
- const knowledgeSourceContent = spaceTrim(args[0] || '');
8406
+ const knowledgeSourceContent = spaceTrim$1(args[0] || '');
6420
8407
  if (knowledgeSourceContent === '') {
6421
8408
  throw new ParseError(`Source is not defined`);
6422
8409
  }
@@ -6560,7 +8547,7 @@ const sectionCommandParser = {
6560
8547
  normalized = normalized.split('DIALOGUE').join('DIALOG');
6561
8548
  const taskTypes = SectionTypes.filter((sectionType) => normalized.includes(sectionType.split('_TASK').join('')));
6562
8549
  if (taskTypes.length !== 1) {
6563
- throw new ParseError(spaceTrim((block) => `
8550
+ throw new ParseError(spaceTrim$1((block) => `
6564
8551
  Unknown section type "${normalized}"
6565
8552
 
6566
8553
  Supported section types are:
@@ -6580,7 +8567,7 @@ const sectionCommandParser = {
6580
8567
  */
6581
8568
  $applyToTaskJson(command, $taskJson, $pipelineJson) {
6582
8569
  if ($taskJson.isSectionTypeSet === true) {
6583
- throw new ParseError(spaceTrim(`
8570
+ throw new ParseError(spaceTrim$1(`
6584
8571
  Section type is already defined in the section.
6585
8572
  It can be defined only once.
6586
8573
  `));
@@ -6928,7 +8915,7 @@ const expectCommandParser = {
6928
8915
  /**
6929
8916
  * Description of the FORMAT command
6930
8917
  */
6931
- description: spaceTrim(`
8918
+ description: spaceTrim$1(`
6932
8919
  Expect command describes the desired output of the task *(after post-processing)*
6933
8920
  It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.
6934
8921
  `),
@@ -7002,7 +8989,7 @@ const expectCommandParser = {
7002
8989
  }
7003
8990
  catch (error) {
7004
8991
  assertsError(error);
7005
- throw new ParseError(spaceTrim((block) => `
8992
+ throw new ParseError(spaceTrim$1((block) => `
7006
8993
  Invalid FORMAT command
7007
8994
  ${block(error.message)}:
7008
8995
  `));
@@ -7187,7 +9174,7 @@ function validateParameterName(parameterName) {
7187
9174
  if (!(error instanceof ParseError)) {
7188
9175
  throw error;
7189
9176
  }
7190
- throw new ParseError(spaceTrim((block) => `
9177
+ throw new ParseError(spaceTrim$1((block) => `
7191
9178
  ${block(error.message)}
7192
9179
 
7193
9180
  Tried to validate parameter name:
@@ -7246,7 +9233,7 @@ const foreachCommandParser = {
7246
9233
  const assignSign = args[3];
7247
9234
  const formatDefinition = FORMAT_DEFINITIONS.find((formatDefinition) => [formatDefinition.formatName, ...(formatDefinition.aliases || [])].includes(formatName));
7248
9235
  if (formatDefinition === undefined) {
7249
- throw new ParseError(spaceTrim((block) => `
9236
+ throw new ParseError(spaceTrim$1((block) => `
7250
9237
  Unsupported format "${formatName}"
7251
9238
 
7252
9239
  Available formats:
@@ -7258,7 +9245,7 @@ const foreachCommandParser = {
7258
9245
  }
7259
9246
  const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(subformatName));
7260
9247
  if (subvalueParser === undefined) {
7261
- throw new ParseError(spaceTrim((block) => `
9248
+ throw new ParseError(spaceTrim$1((block) => `
7262
9249
  Unsupported subformat name "${subformatName}" for format "${formatName}"
7263
9250
 
7264
9251
  Available subformat names for format "${formatDefinition.formatName}":
@@ -7306,7 +9293,7 @@ const foreachCommandParser = {
7306
9293
  outputSubparameterName = 'newLine';
7307
9294
  }
7308
9295
  else {
7309
- throw new ParseError(spaceTrim(`
9296
+ throw new ParseError(spaceTrim$1(`
7310
9297
  FOREACH ${formatName} ${subformatName} must specify output subparameter
7311
9298
 
7312
9299
  Correct example:
@@ -7382,7 +9369,7 @@ const formatCommandParser = {
7382
9369
  /**
7383
9370
  * Description of the FORMAT command
7384
9371
  */
7385
- description: spaceTrim(`
9372
+ description: spaceTrim$1(`
7386
9373
  Format command describes the desired output of the task (after post-processing)
7387
9374
  It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.
7388
9375
  `),
@@ -7754,7 +9741,7 @@ const formfactorCommandParser = {
7754
9741
  const formfactorNameCandidate = args[0].toUpperCase();
7755
9742
  const formfactor = FORMFACTOR_DEFINITIONS.find((definition) => [definition.name, ...{ aliasNames: [], ...definition }.aliasNames].includes(formfactorNameCandidate));
7756
9743
  if (formfactor === undefined) {
7757
- throw new ParseError(spaceTrim((block) => `
9744
+ throw new ParseError(spaceTrim$1((block) => `
7758
9745
  Unknown formfactor name "${formfactorNameCandidate}"
7759
9746
 
7760
9747
  Available formfactors:
@@ -7773,7 +9760,7 @@ const formfactorCommandParser = {
7773
9760
  */
7774
9761
  $applyToPipelineJson(command, $pipelineJson) {
7775
9762
  if ($pipelineJson.formfactorName !== undefined && $pipelineJson.formfactorName !== command.formfactorName) {
7776
- throw new ParseError(spaceTrim(`
9763
+ throw new ParseError(spaceTrim$1(`
7777
9764
  Redefinition of \`FORMFACTOR\` in the pipeline head
7778
9765
 
7779
9766
  You have used:
@@ -7916,7 +9903,7 @@ const modelCommandParser = {
7916
9903
  */
7917
9904
  parse(input) {
7918
9905
  const { args, normalized } = input;
7919
- const availableVariantsMessage = spaceTrim((block) => `
9906
+ const availableVariantsMessage = spaceTrim$1((block) => `
7920
9907
  Available variants are:
7921
9908
  ${block(MODEL_VARIANTS.map((variantName) => `- ${variantName}${variantName !== 'EMBEDDING' ? '' : ' (Not available in pipeline)'}`).join('\n'))}
7922
9909
  `);
@@ -7938,14 +9925,14 @@ const modelCommandParser = {
7938
9925
  // <- Note: [🤖]
7939
9926
  }
7940
9927
  else if (normalized.startsWith('MODEL_VARIANT_EMBED')) {
7941
- spaceTrim((block) => `
9928
+ spaceTrim$1((block) => `
7942
9929
  Embedding model can not be used in pipeline
7943
9930
 
7944
9931
  ${block(availableVariantsMessage)}
7945
9932
  `);
7946
9933
  }
7947
9934
  else {
7948
- throw new ParseError(spaceTrim((block) => `
9935
+ throw new ParseError(spaceTrim$1((block) => `
7949
9936
  Unknown model variant in command:
7950
9937
 
7951
9938
  ${block(availableVariantsMessage)}
@@ -7960,7 +9947,7 @@ const modelCommandParser = {
7960
9947
  };
7961
9948
  }
7962
9949
  else {
7963
- throw new ParseError(spaceTrim((block) => `
9950
+ throw new ParseError(spaceTrim$1((block) => `
7964
9951
  Unknown model key in command.
7965
9952
 
7966
9953
  Supported model keys are:
@@ -7987,7 +9974,7 @@ const modelCommandParser = {
7987
9974
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
7988
9975
  }
7989
9976
  else {
7990
- throw new ParseError(spaceTrim(`
9977
+ throw new ParseError(spaceTrim$1(`
7991
9978
  Redefinition of \`MODEL ${command.key}\` in the pipeline head
7992
9979
 
7993
9980
  You have used:
@@ -8019,7 +10006,7 @@ const modelCommandParser = {
8019
10006
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
8020
10007
  }
8021
10008
  else {
8022
- throw new ParseError(spaceTrim(`
10009
+ throw new ParseError(spaceTrim$1(`
8023
10010
  Redefinition of MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}"
8024
10011
 
8025
10012
  You have used:
@@ -8029,7 +10016,7 @@ const modelCommandParser = {
8029
10016
  }
8030
10017
  }
8031
10018
  if (command.value === ($pipelineJson.defaultModelRequirements || {})[command.key]) {
8032
- console.log(spaceTrim(`
10019
+ console.log(spaceTrim$1(`
8033
10020
  Setting MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}" to the same value as in the pipeline head
8034
10021
 
8035
10022
  In pipeline head:
@@ -8112,7 +10099,7 @@ const parameterCommandParser = {
8112
10099
  // <- TODO: When [🥶] fixed, change to:
8113
10100
  // > const parameterDescriptionRaw = rawArgs.split(parameterNameRaw).join('').trim();
8114
10101
  if (parameterDescriptionRaw && parameterDescriptionRaw.match(/\{(?<embeddedParameterName>[a-z0-9_]+)\}/im)) {
8115
- throw new ParseError(spaceTrim((block) => `
10102
+ throw new ParseError(spaceTrim$1((block) => `
8116
10103
  Parameter \`{${parameterNameRaw}}\` can not contain another parameter in description
8117
10104
 
8118
10105
  The description:
@@ -8294,7 +10281,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
8294
10281
  persona.description = personaDescription;
8295
10282
  return;
8296
10283
  }
8297
- console.warn(spaceTrim(`
10284
+ console.warn(spaceTrim$1(`
8298
10285
 
8299
10286
  Persona "${personaName}" is defined multiple times with different description:
8300
10287
 
@@ -8305,7 +10292,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
8305
10292
  ${personaDescription}
8306
10293
 
8307
10294
  `));
8308
- persona.description += spaceTrim('\n\n' + personaDescription);
10295
+ persona.description += spaceTrim$1('\n\n' + personaDescription);
8309
10296
  }
8310
10297
 
8311
10298
  /**
@@ -8670,7 +10657,7 @@ const COMMANDS = [
8670
10657
  function getParserForCommand(command) {
8671
10658
  const commandParser = COMMANDS.find((commandParser) => commandParser.name === command.type);
8672
10659
  if (commandParser === undefined) {
8673
- throw new UnexpectedError(spaceTrim$1((block) => `
10660
+ throw new UnexpectedError(spaceTrim((block) => `
8674
10661
  Command ${command.type} parser is not found
8675
10662
 
8676
10663
  ${block(JSON.stringify(command, null, 4)
@@ -8746,7 +10733,7 @@ function parseCommand(raw, usagePlace) {
8746
10733
  .map(removeMarkdownFormatting)
8747
10734
  .map((item) => item.trim());
8748
10735
  if (items.length === 0 || items[0] === '') {
8749
- throw new ParseError(spaceTrim$1((block) => `
10736
+ throw new ParseError(spaceTrim((block) => `
8750
10737
  Malformed command:
8751
10738
  - ${raw}
8752
10739
 
@@ -8782,7 +10769,7 @@ function parseCommand(raw, usagePlace) {
8782
10769
  return command;
8783
10770
  }
8784
10771
  }
8785
- throw new ParseError(spaceTrim$1((block) => `
10772
+ throw new ParseError(spaceTrim((block) => `
8786
10773
  Malformed or unknown command:
8787
10774
  - ${raw}
8788
10775
 
@@ -8833,7 +10820,7 @@ function parseCommandVariant(input) {
8833
10820
  if (!(error instanceof ParseError)) {
8834
10821
  throw error;
8835
10822
  }
8836
- throw new ParseError(spaceTrim$1((block) => `
10823
+ throw new ParseError(spaceTrim((block) => `
8837
10824
  Invalid ${commandName} command:
8838
10825
 
8839
10826
  Your command:
@@ -9107,7 +11094,7 @@ const SUPPORTED_SCRIPT_LANGUAGES = ['javascript', 'typescript', 'python'];
9107
11094
  * @public exported from `@promptbook/markdown-utils`
9108
11095
  */
9109
11096
  function removeMarkdownComments(content) {
9110
- return spaceTrim$1(content.replace(/<!--(.*?)-->/gs, ''));
11097
+ return spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
9111
11098
  }
9112
11099
 
9113
11100
  /**
@@ -9118,7 +11105,7 @@ function removeMarkdownComments(content) {
9118
11105
  */
9119
11106
  function isFlatPipeline(pipelineString) {
9120
11107
  pipelineString = removeMarkdownComments(pipelineString);
9121
- pipelineString = spaceTrim(pipelineString);
11108
+ pipelineString = spaceTrim$1(pipelineString);
9122
11109
  const isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
9123
11110
  //const isLastLineReturnStatement = pipelineString.split('\n').pop()!.split('`').join('').startsWith('->');
9124
11111
  const isBacktickBlockUsed = pipelineString.includes('```');
@@ -9156,19 +11143,19 @@ function deflatePipeline(pipelineString) {
9156
11143
  returnStatement = `-> {${DEFAULT_BOOK_OUTPUT_PARAMETER_NAME}}`;
9157
11144
  pipelineStringLines.push(potentialReturnStatement);
9158
11145
  }
9159
- const prompt = spaceTrim(pipelineStringLines.join('\n'));
11146
+ const prompt = spaceTrim$1(pipelineStringLines.join('\n'));
9160
11147
  let quotedPrompt;
9161
11148
  if (prompt.split('\n').length <= 1) {
9162
11149
  quotedPrompt = `> ${prompt}`;
9163
11150
  }
9164
11151
  else {
9165
- quotedPrompt = spaceTrim((block) => `
11152
+ quotedPrompt = spaceTrim$1((block) => `
9166
11153
  \`\`\`
9167
11154
  ${block(prompt.split('`').join('\\`'))}
9168
11155
  \`\`\`
9169
11156
  `);
9170
11157
  }
9171
- pipelineString = validatePipelineString(spaceTrim((block) => `
11158
+ pipelineString = validatePipelineString(spaceTrim$1((block) => `
9172
11159
  # ${DEFAULT_BOOK_TITLE}
9173
11160
 
9174
11161
  ## Prompt
@@ -9232,7 +11219,7 @@ function extractAllListItemsFromMarkdown(markdown) {
9232
11219
  function extractOneBlockFromMarkdown(markdown) {
9233
11220
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
9234
11221
  if (codeBlocks.length !== 1) {
9235
- throw new ParseError(spaceTrim((block) => `
11222
+ throw new ParseError(spaceTrim$1((block) => `
9236
11223
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
9237
11224
 
9238
11225
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -9257,7 +11244,7 @@ function parseMarkdownSection(value) {
9257
11244
  }
9258
11245
  const title = lines[0].replace(/^#+\s*/, '');
9259
11246
  const level = (_b = (_a = lines[0].match(/^#+/)) === null || _a === void 0 ? void 0 : _a[0].length) !== null && _b !== void 0 ? _b : 0;
9260
- const content = spaceTrim(lines.slice(1).join('\n'));
11247
+ const content = spaceTrim$1(lines.slice(1).join('\n'));
9261
11248
  if (level < 1 || level > 6) {
9262
11249
  throw new ParseError('Markdown section must have heading level between 1 and 6');
9263
11250
  }
@@ -9285,7 +11272,7 @@ function splitMarkdownIntoSections(markdown) {
9285
11272
  if (buffer.length === 0) {
9286
11273
  return;
9287
11274
  }
9288
- let section = spaceTrim(buffer.join('\n'));
11275
+ let section = spaceTrim$1(buffer.join('\n'));
9289
11276
  if (section === '') {
9290
11277
  return;
9291
11278
  }
@@ -9360,7 +11347,7 @@ function flattenMarkdown(markdown) {
9360
11347
  flattenedMarkdown += `## ${title}` + `\n\n`;
9361
11348
  flattenedMarkdown += content + `\n\n`; // <- [🧠] Maybe 3 new lines?
9362
11349
  }
9363
- return spaceTrim(flattenedMarkdown);
11350
+ return spaceTrim$1(flattenedMarkdown);
9364
11351
  }
9365
11352
  /**
9366
11353
  * TODO: [🏛] This can be part of markdown builder
@@ -9422,7 +11409,7 @@ function parsePipeline(pipelineString) {
9422
11409
  if (pipelineString.startsWith('#!')) {
9423
11410
  const [shebangLine, ...restLines] = pipelineString.split('\n');
9424
11411
  if (!(shebangLine || '').includes('ptbk')) {
9425
- throw new ParseError(spaceTrim$1((block) => `
11412
+ throw new ParseError(spaceTrim((block) => `
9426
11413
  It seems that you try to parse a book file which has non-standard shebang line for book files:
9427
11414
  Shebang line must contain 'ptbk'
9428
11415
 
@@ -9438,7 +11425,7 @@ function parsePipeline(pipelineString) {
9438
11425
  pipelineString = validatePipelineString(restLines.join('\n'));
9439
11426
  }
9440
11427
  pipelineString = removeMarkdownComments(pipelineString);
9441
- pipelineString = spaceTrim$1(pipelineString);
11428
+ pipelineString = spaceTrim(pipelineString);
9442
11429
  // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
9443
11430
  pipelineString = deflatePipeline(pipelineString);
9444
11431
  // ==============
@@ -9450,7 +11437,7 @@ function parsePipeline(pipelineString) {
9450
11437
  // ==============
9451
11438
  // Note: 1️⃣◽4️⃣ Check markdown structure
9452
11439
  if (pipelineHead === undefined) {
9453
- throw new UnexpectedError(spaceTrim$1((block) => `
11440
+ throw new UnexpectedError(spaceTrim((block) => `
9454
11441
  Pipeline head is not defined
9455
11442
 
9456
11443
  ${block(getPipelineIdentification())}
@@ -9459,7 +11446,7 @@ function parsePipeline(pipelineString) {
9459
11446
  `));
9460
11447
  }
9461
11448
  if (pipelineHead.level !== 1) {
9462
- throw new UnexpectedError(spaceTrim$1((block) => `
11449
+ throw new UnexpectedError(spaceTrim((block) => `
9463
11450
  Pipeline head is not h1
9464
11451
 
9465
11452
  ${block(getPipelineIdentification())}
@@ -9468,7 +11455,7 @@ function parsePipeline(pipelineString) {
9468
11455
  `));
9469
11456
  }
9470
11457
  if (!pipelineSections.every((section) => section.level === 2)) {
9471
- throw new UnexpectedError(spaceTrim$1((block) => `
11458
+ throw new UnexpectedError(spaceTrim((block) => `
9472
11459
  Not every pipeline section is h2
9473
11460
 
9474
11461
  ${block(getPipelineIdentification())}
@@ -9481,7 +11468,7 @@ function parsePipeline(pipelineString) {
9481
11468
  const defineParam = (parameterCommand) => {
9482
11469
  const { parameterName, parameterDescription, isInput, isOutput } = parameterCommand;
9483
11470
  if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
9484
- throw new ParseError(spaceTrim$1((block) => `
11471
+ throw new ParseError(spaceTrim((block) => `
9485
11472
  Parameter name {${parameterName}} is reserved and cannot be used as resulting parameter name
9486
11473
 
9487
11474
  ${block(getPipelineIdentification())}
@@ -9492,7 +11479,7 @@ function parsePipeline(pipelineString) {
9492
11479
  existingParameter.description &&
9493
11480
  existingParameter.description !== parameterDescription &&
9494
11481
  parameterDescription) {
9495
- throw new ParseError(spaceTrim$1((block) => `
11482
+ throw new ParseError(spaceTrim((block) => `
9496
11483
  Parameter \`{${parameterName}}\` is defined multiple times with different description:
9497
11484
 
9498
11485
  ${block(getPipelineIdentification())}
@@ -9530,7 +11517,7 @@ function parsePipeline(pipelineString) {
9530
11517
  description = description.split(/^>.*$/gm).join('');
9531
11518
  //Note: Remove lists and return statement - TODO: [🎾] Make util (exported from `@promptbool/utils`)
9532
11519
  description = description.split(/^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm).join('');
9533
- description = spaceTrim$1(description);
11520
+ description = spaceTrim(description);
9534
11521
  if (description === '') {
9535
11522
  description = undefined;
9536
11523
  }
@@ -9541,7 +11528,7 @@ function parsePipeline(pipelineString) {
9541
11528
  const command = parseCommand(listItem, 'PIPELINE_HEAD');
9542
11529
  const commandParser = getParserForCommand(command);
9543
11530
  if (commandParser.isUsedInPipelineHead !== true /* <- Note: [🦦][4] */) {
9544
- throw new ParseError(spaceTrim$1((block) => `
11531
+ throw new ParseError(spaceTrim((block) => `
9545
11532
  Command \`${command.type}\` is not allowed in the head of the pipeline ONLY at the pipeline task
9546
11533
 
9547
11534
  ${block(getPipelineIdentification())}
@@ -9555,7 +11542,7 @@ function parsePipeline(pipelineString) {
9555
11542
  if (!(error instanceof ParseError)) {
9556
11543
  throw error;
9557
11544
  }
9558
- throw new ParseError(spaceTrim$1((block) => `
11545
+ throw new ParseError(spaceTrim((block) => `
9559
11546
  Command ${command.type} failed to apply to the pipeline
9560
11547
 
9561
11548
  The error:
@@ -9608,7 +11595,7 @@ function parsePipeline(pipelineString) {
9608
11595
  description = description.split(/^>.*$/gm).join('');
9609
11596
  //Note: Remove lists and return statement - TODO: [🎾]
9610
11597
  description = description.split(/^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm).join('');
9611
- description = spaceTrim$1(description);
11598
+ description = spaceTrim(description);
9612
11599
  if (description === '') {
9613
11600
  description = undefined;
9614
11601
  }
@@ -9642,7 +11629,7 @@ function parsePipeline(pipelineString) {
9642
11629
  for (const { listItem, command } of commands) {
9643
11630
  const commandParser = getParserForCommand(command);
9644
11631
  if (commandParser.isUsedInPipelineTask !== true /* <- Note: [🦦][4] */) {
9645
- throw new ParseError(spaceTrim$1((block) => `
11632
+ throw new ParseError(spaceTrim((block) => `
9646
11633
  Command \`${command.type}\` is not allowed in the task of the promptbook ONLY at the pipeline head
9647
11634
 
9648
11635
  ${block(getPipelineIdentification())}
@@ -9657,7 +11644,7 @@ function parsePipeline(pipelineString) {
9657
11644
  if (!(error instanceof ParseError)) {
9658
11645
  throw error;
9659
11646
  }
9660
- throw new ParseError(spaceTrim$1((block) => `
11647
+ throw new ParseError(spaceTrim((block) => `
9661
11648
  Command \`${command.type}\` failed to apply to the task
9662
11649
 
9663
11650
  The error:
@@ -9688,14 +11675,14 @@ function parsePipeline(pipelineString) {
9688
11675
  // TODO: [🍧] Should be done in SECTION command
9689
11676
  if ($taskJson.taskType === 'SCRIPT_TASK') {
9690
11677
  if (!language) {
9691
- throw new ParseError(spaceTrim$1((block) => `
11678
+ throw new ParseError(spaceTrim((block) => `
9692
11679
  You must specify the language of the script in the \`SCRIPT\` task
9693
11680
 
9694
11681
  ${block(getPipelineIdentification())}
9695
11682
  `));
9696
11683
  }
9697
11684
  if (!SUPPORTED_SCRIPT_LANGUAGES.includes(language)) {
9698
- throw new ParseError(spaceTrim$1((block) => `
11685
+ throw new ParseError(spaceTrim((block) => `
9699
11686
  Script language ${language} is not supported.
9700
11687
 
9701
11688
  Supported languages are:
@@ -9857,7 +11844,7 @@ function addAutoGeneratedSection(content, options) {
9857
11844
  const warningLine = `<!-- ${GENERATOR_WARNING} -->`;
9858
11845
  const sectionRegex = new RegExp(`<!--${sectionName}-->([\\s\\S]*?)<!--/${sectionName}-->`, 'g');
9859
11846
  const sectionMatch = content.match(sectionRegex);
9860
- const contentToInsert = spaceTrim$1((block) => `
11847
+ const contentToInsert = spaceTrim((block) => `
9861
11848
  <!--${sectionName}-->
9862
11849
  ${block(warningLine)}
9863
11850
  ${block(sectionContent)}
@@ -9870,7 +11857,7 @@ function addAutoGeneratedSection(content, options) {
9870
11857
  const placeForSection = removeMarkdownComments(content).match(/^##.*$/im);
9871
11858
  if (placeForSection !== null) {
9872
11859
  const [heading] = placeForSection;
9873
- return content.replace(heading, spaceTrim$1((block) => `
11860
+ return content.replace(heading, spaceTrim((block) => `
9874
11861
  ${block(contentToInsert)}
9875
11862
 
9876
11863
  ${block(heading)}
@@ -9879,7 +11866,7 @@ function addAutoGeneratedSection(content, options) {
9879
11866
  console.warn(`No place where to put the section <!--${sectionName}-->, using the end of the file`);
9880
11867
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
9881
11868
  // <- TODO: [🏮] Some better way how to get warnings from pipeline parsing / logic
9882
- return spaceTrim$1((block) => `
11869
+ return spaceTrim((block) => `
9883
11870
  ${block(content)}
9884
11871
 
9885
11872
  ${block(contentToInsert)}
@@ -9955,7 +11942,7 @@ function renderPromptbookMermaid(pipelineJson, options) {
9955
11942
  .filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
9956
11943
  .map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
9957
11944
  .join('\n');
9958
- const promptbookMermaid = spaceTrim$1((block) => `
11945
+ const promptbookMermaid = spaceTrim((block) => `
9959
11946
 
9960
11947
  %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
9961
11948
 
@@ -10011,7 +11998,7 @@ async function prettifyPipelineString(pipelineString, options) {
10011
11998
  return { href: `#${task.name}`, title: task.title };
10012
11999
  },
10013
12000
  });
10014
- const promptbookMermaidBlock = spaceTrim$1((block) => `
12001
+ const promptbookMermaidBlock = spaceTrim((block) => `
10015
12002
  \`\`\`mermaid
10016
12003
  ${block(promptbookMermaid)}
10017
12004
  \`\`\`
@@ -10049,7 +12036,7 @@ class CallbackInterfaceTools {
10049
12036
  async promptDialog(options) {
10050
12037
  const answer = await this.options.callback(options);
10051
12038
  if (this.options.isVerbose) {
10052
- console.info(spaceTrim$1((block) => `
12039
+ console.info(spaceTrim((block) => `
10053
12040
  📖 ${block(options.promptTitle)}
10054
12041
  👤 ${block(answer)}
10055
12042
  `));
@@ -10188,7 +12175,7 @@ function countWorkingDuration(items) {
10188
12175
  function executionReportJsonToString(executionReportJson, options) {
10189
12176
  var _a, _b, _c, _d, _e, _f;
10190
12177
  const { taxRate, chartsWidth } = { ...ExecutionReportStringOptionsDefaults, ...(options || {}) };
10191
- let executionReportString = spaceTrim$1((block) => `
12178
+ let executionReportString = spaceTrim((block) => `
10192
12179
  # ${executionReportJson.title || 'Execution report'}
10193
12180
 
10194
12181
  ${block(executionReportJson.description || '')}
@@ -10310,7 +12297,7 @@ function executionReportJsonToString(executionReportJson, options) {
10310
12297
  if (just(true)) {
10311
12298
  executionReportString +=
10312
12299
  '\n\n\n\n' +
10313
- spaceTrim$1((block) => {
12300
+ spaceTrim((block) => {
10314
12301
  var _a;
10315
12302
  return `
10316
12303
 
@@ -10329,7 +12316,7 @@ function executionReportJsonToString(executionReportJson, options) {
10329
12316
  executionReportString += '*No result*';
10330
12317
  }
10331
12318
  else if (typeof promptExecution.result.content === 'string') {
10332
- executionReportString += spaceTrim$1((block) => `
12319
+ executionReportString += spaceTrim((block) => `
10333
12320
  \`\`\`
10334
12321
  ${block(escapeMarkdownBlock(promptExecution.result.content))}
10335
12322
  \`\`\`
@@ -10342,7 +12329,7 @@ function executionReportJsonToString(executionReportJson, options) {
10342
12329
  if (promptExecution.error && promptExecution.error.message) {
10343
12330
  executionReportString +=
10344
12331
  '\n\n\n\n' +
10345
- spaceTrim$1((block) => `
12332
+ spaceTrim((block) => `
10346
12333
 
10347
12334
  ### Error
10348
12335
 
@@ -10418,7 +12405,7 @@ function usageToHuman(usage) {
10418
12405
  // Note: For negligible usage, we report at least something
10419
12406
  reportItems.push('Negligible');
10420
12407
  }
10421
- return spaceTrim((block) => `
12408
+ return spaceTrim$1((block) => `
10422
12409
  Usage:
10423
12410
  ${block(reportItems.map((item) => `- ${item}`).join('\n'))}
10424
12411
  `);
@@ -10663,13 +12650,13 @@ function $registeredLlmToolsMessage() {
10663
12650
  });
10664
12651
  const usedEnvMessage = `Unknown \`.env\` file` ;
10665
12652
  if (metadata.length === 0) {
10666
- return spaceTrim((block) => `
12653
+ return spaceTrim$1((block) => `
10667
12654
  No LLM providers are available.
10668
12655
 
10669
12656
  ${block(usedEnvMessage)}
10670
12657
  `);
10671
12658
  }
10672
- return spaceTrim((block) => `
12659
+ return spaceTrim$1((block) => `
10673
12660
 
10674
12661
  ${block(usedEnvMessage)}
10675
12662
 
@@ -10715,7 +12702,7 @@ function $registeredLlmToolsMessage() {
10715
12702
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
10716
12703
  }
10717
12704
  }
10718
- let providerMessage = spaceTrim(`
12705
+ let providerMessage = spaceTrim$1(`
10719
12706
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
10720
12707
  ${morePieces.join('; ')}
10721
12708
  `);
@@ -10761,7 +12748,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
10761
12748
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
10762
12749
  if (registeredItem === undefined) {
10763
12750
  // console.log('$llmToolsRegister.list()', $llmToolsRegister.list());
10764
- throw new Error(spaceTrim((block) => `
12751
+ throw new Error(spaceTrim$1((block) => `
10765
12752
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
10766
12753
  Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
10767
12754
 
@@ -10898,7 +12885,7 @@ function cacheLlmTools(llmTools, options = {}) {
10898
12885
  let normalizedContent = content;
10899
12886
  normalizedContent = normalizedContent.replace(/\s+/g, ' ');
10900
12887
  normalizedContent = normalizedContent.split('\r\n').join('\n');
10901
- normalizedContent = spaceTrim(normalizedContent);
12888
+ normalizedContent = spaceTrim$1(normalizedContent);
10902
12889
  // Note: Do not need to save everything in the cache, just the relevant parameters
10903
12890
  const relevantParameterNames = extractParameterNames(content);
10904
12891
  const relevantParameters = Object.fromEntries(Object.entries(parameters).filter(([key]) => relevantParameterNames.has(key)));
@@ -11578,7 +13565,7 @@ function isValidPipelineString(pipelineString) {
11578
13565
  */
11579
13566
  function prompt(strings, ...values) {
11580
13567
  if (values.length === 0) {
11581
- return spaceTrim(strings.join(''));
13568
+ return spaceTrim$1(strings.join(''));
11582
13569
  }
11583
13570
  const stringsWithHiddenParameters = strings.map((stringsItem) =>
11584
13571
  // TODO: [0] DRY
@@ -11589,7 +13576,7 @@ function prompt(strings, ...values) {
11589
13576
  let pipelineString = stringsWithHiddenParameters.reduce((result, stringsItem, i) => placeholderParameterNames[i] === undefined
11590
13577
  ? `${result}${stringsItem}`
11591
13578
  : `${result}${stringsItem}{${placeholderParameterNames[i]}}`, '');
11592
- pipelineString = spaceTrim(pipelineString);
13579
+ pipelineString = spaceTrim$1(pipelineString);
11593
13580
  try {
11594
13581
  pipelineString = templateParameters(pipelineString, parameters);
11595
13582
  }
@@ -11598,7 +13585,7 @@ function prompt(strings, ...values) {
11598
13585
  throw error;
11599
13586
  }
11600
13587
  console.error({ pipelineString, parameters, placeholderParameterNames, error });
11601
- throw new UnexpectedError(spaceTrim((block) => `
13588
+ throw new UnexpectedError(spaceTrim$1((block) => `
11602
13589
  Internal error in prompt template literal
11603
13590
 
11604
13591
  ${block(JSON.stringify({ strings, values }, null, 4))}}
@@ -11632,18 +13619,28 @@ function prompt(strings, ...values) {
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
- throw new Error(spaceTrim(`
13625
+ throw new Error(spaceTrim$1(`
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$1(`
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
@@ -11973,5 +13970,5 @@ class PrefixStorage {
11973
13970
  }
11974
13971
  }
11975
13972
 
11976
- export { $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, AbstractFormatError, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_TITLE, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NonTaskSectionTypes, NotFoundError, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, REMOTE_SERVER_URLS, RESERVED_PARAMETER_NAMES, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, addUsage, book, cacheLlmTools, collectionToJson, compilePipeline, computeCosineSimilarity, countUsage, createCollectionFromJson, createCollectionFromPromise, createCollectionFromUrl, createLlmToolsFromConfiguration, createPipelineExecutor, createSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, getPipelineInterface, identificationToPromptbookToken, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, parsePipeline, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validatePipeline, validatePipelineString };
13973
+ export { $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, AbstractFormatError, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_TITLE, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NonTaskSectionTypes, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, REMOTE_SERVER_URLS, RESERVED_PARAMETER_NAMES, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, addUsage, book, cacheLlmTools, collectionToJson, compilePipeline, computeCosineSimilarity, countUsage, createAgentModelRequirements, createBasicAgentModelRequirements, createCollectionFromJson, createCollectionFromPromise, createCollectionFromUrl, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineExecutor, createSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, getAllCommitmentDefinitions, getAllCommitmentTypes, getCommitmentDefinition, getPipelineInterface, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, parseAgentSource, parsePipeline, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
11977
13974
  //# sourceMappingURL=index.es.js.map