@promptbook/core 0.105.0-0 → 0.105.0-10

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 (71) hide show
  1. package/README.md +36 -77
  2. package/esm/index.es.js +2226 -401
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/servers.d.ts +6 -0
  5. package/esm/typings/src/_packages/browser.index.d.ts +2 -0
  6. package/esm/typings/src/_packages/core.index.d.ts +4 -0
  7. package/esm/typings/src/_packages/types.index.d.ts +16 -0
  8. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  9. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +15 -3
  10. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +11 -1
  11. package/esm/typings/src/book-2.0/agent-source/communication-samples.test.d.ts +1 -0
  12. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.blocks.test.d.ts +1 -0
  13. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.import.test.d.ts +1 -0
  14. package/esm/typings/src/book-2.0/agent-source/parseAgentSource.import.test.d.ts +1 -0
  15. package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.blocks.test.d.ts +1 -0
  16. package/esm/typings/src/book-components/Chat/AgentChat/AgentChatProps.d.ts +5 -0
  17. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +15 -1
  18. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +20 -9
  19. package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +5 -0
  20. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +43 -0
  21. package/esm/typings/src/commitments/NOTE/NOTE.d.ts +2 -2
  22. package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +4 -0
  23. package/esm/typings/src/commitments/USE_SEARCH_ENGINE/USE_SEARCH_ENGINE.d.ts +10 -0
  24. package/esm/typings/src/commitments/USE_SEARCH_ENGINE/USE_SEARCH_ENGINE.test.d.ts +1 -0
  25. package/esm/typings/src/commitments/USE_TIME/USE_TIME.d.ts +44 -0
  26. package/esm/typings/src/commitments/USE_TIME/USE_TIME.test.d.ts +1 -0
  27. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +14 -0
  28. package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +14 -0
  29. package/esm/typings/src/commitments/index.d.ts +17 -2
  30. package/esm/typings/src/config.d.ts +1 -0
  31. package/esm/typings/src/execution/LlmExecutionTools.d.ts +3 -1
  32. package/esm/typings/src/import-plugins/$fileImportPlugins.d.ts +7 -0
  33. package/esm/typings/src/import-plugins/AgentFileImportPlugin.d.ts +7 -0
  34. package/esm/typings/src/import-plugins/FileImportPlugin.d.ts +24 -0
  35. package/esm/typings/src/import-plugins/JsonFileImportPlugin.d.ts +7 -0
  36. package/esm/typings/src/import-plugins/TextFileImportPlugin.d.ts +7 -0
  37. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +2 -1
  38. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +2 -2
  39. package/esm/typings/src/llm-providers/agent/Agent.d.ts +14 -2
  40. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +3 -1
  41. package/esm/typings/src/llm-providers/agent/AgentOptions.d.ts +7 -0
  42. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +1 -0
  43. package/esm/typings/src/llm-providers/agent/RemoteAgentOptions.d.ts +1 -1
  44. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +10 -0
  45. package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +7 -0
  46. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -1
  47. package/esm/typings/src/scripting/javascript/JavascriptExecutionToolsOptions.d.ts +6 -1
  48. package/esm/typings/src/search-engines/SearchEngine.d.ts +1 -1
  49. package/esm/typings/src/search-engines/_index.d.ts +6 -0
  50. package/esm/typings/src/search-engines/bing/BingSearchEngine.d.ts +1 -1
  51. package/esm/typings/src/search-engines/dummy/DummySearchEngine.d.ts +1 -1
  52. package/esm/typings/src/search-engines/google/GoogleSearchEngine.d.ts +18 -0
  53. package/esm/typings/src/search-engines/serp/SerpSearchEngine.d.ts +15 -0
  54. package/esm/typings/src/speech-recognition/BrowserSpeechRecognition.d.ts +21 -0
  55. package/esm/typings/src/speech-recognition/OpenAiSpeechRecognition.d.ts +32 -0
  56. package/esm/typings/src/types/ModelRequirements.d.ts +6 -12
  57. package/esm/typings/src/types/SpeechRecognition.d.ts +58 -0
  58. package/esm/typings/src/types/typeAliases.d.ts +4 -0
  59. package/esm/typings/src/utils/execCommand/$execCommandNormalizeOptions.d.ts +2 -3
  60. package/esm/typings/src/utils/execCommand/ExecCommandOptions.d.ts +7 -1
  61. package/esm/typings/src/utils/misc/linguisticHash.d.ts +6 -0
  62. package/esm/typings/src/utils/misc/linguisticHash.test.d.ts +1 -0
  63. package/esm/typings/src/utils/organization/keepImported.d.ts +9 -0
  64. package/esm/typings/src/utils/organization/keepTypeImported.d.ts +0 -1
  65. package/esm/typings/src/utils/random/$generateBookBoilerplate.d.ts +4 -0
  66. package/esm/typings/src/utils/random/$randomAgentPersona.d.ts +2 -1
  67. package/esm/typings/src/utils/random/$randomAgentRule.d.ts +14 -0
  68. package/esm/typings/src/version.d.ts +1 -1
  69. package/package.json +1 -1
  70. package/umd/index.umd.js +2227 -400
  71. package/umd/index.umd.js.map +1 -1
package/esm/index.es.js CHANGED
@@ -27,7 +27,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
27
27
  * @generated
28
28
  * @see https://github.com/webgptorg/promptbook
29
29
  */
30
- const PROMPTBOOK_ENGINE_VERSION = '0.105.0-0';
30
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-10';
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
@@ -992,6 +992,7 @@ const PROMPTBOOK_SYNTAX_COLORS = {
992
992
  SEPARATOR: Color.fromHex('#cccccc'),
993
993
  COMMITMENT: Color.fromHex('#DA0F78'),
994
994
  PARAMETER: Color.fromHex('#8e44ad'),
995
+ CODE_BLOCK: Color.fromHex('#7700ffff'),
995
996
  };
996
997
  // <- TODO: [🧠][🈵] Using `Color` here increases the package size approx 3kb, maybe remove it
997
998
  /**
@@ -3649,74 +3650,90 @@ function addUsage(...usageItems) {
3649
3650
  * in real-time through an observable.
3650
3651
  *
3651
3652
  * @param llmTools - The LLM tools to be intercepted and tracked
3652
- * @returns An augmented version of the tools that includes usage tracking capabilities
3653
+ * @returns Full proxy of the tools with added usage tracking capabilities
3653
3654
  * @public exported from `@promptbook/core`
3654
3655
  */
3655
3656
  function countUsage(llmTools) {
3656
3657
  let totalUsage = ZERO_USAGE;
3657
3658
  const spending = new Subject();
3658
- const proxyTools = {
3659
- get title() {
3660
- return `${llmTools.title} (+usage)`;
3661
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3662
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3663
- },
3664
- get description() {
3665
- return `${llmTools.description} (+usage)`;
3666
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3667
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3668
- },
3669
- checkConfiguration() {
3670
- return /* not await */ llmTools.checkConfiguration();
3671
- },
3672
- listModels() {
3673
- return /* not await */ llmTools.listModels();
3674
- },
3675
- spending() {
3676
- return spending.asObservable();
3677
- },
3678
- getTotalUsage() {
3679
- // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
3680
- return totalUsage;
3659
+ // Create a Proxy to intercept all property access and ensure full proxying of all properties
3660
+ const proxyTools = new Proxy(llmTools, {
3661
+ get(target, prop, receiver) {
3662
+ // Handle title property
3663
+ if (prop === 'title') {
3664
+ return `${target.title} (+usage)`;
3665
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3666
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3667
+ }
3668
+ // Handle description property
3669
+ if (prop === 'description') {
3670
+ return `${target.description} (+usage)`;
3671
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3672
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3673
+ }
3674
+ // Handle spending method (new method added by this wrapper)
3675
+ if (prop === 'spending') {
3676
+ return () => {
3677
+ return spending.asObservable();
3678
+ };
3679
+ }
3680
+ // Handle getTotalUsage method (new method added by this wrapper)
3681
+ if (prop === 'getTotalUsage') {
3682
+ // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
3683
+ return () => {
3684
+ return totalUsage;
3685
+ };
3686
+ }
3687
+ // Handle callChatModel method with usage counting
3688
+ if (prop === 'callChatModel' && target.callChatModel !== undefined) {
3689
+ return async (prompt) => {
3690
+ // console.info('[🚕] callChatModel through countTotalUsage');
3691
+ const promptResult = await target.callChatModel(prompt);
3692
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3693
+ spending.next(promptResult.usage);
3694
+ return promptResult;
3695
+ };
3696
+ }
3697
+ // Handle callCompletionModel method with usage counting
3698
+ if (prop === 'callCompletionModel' && target.callCompletionModel !== undefined) {
3699
+ return async (prompt) => {
3700
+ // console.info('[🚕] callCompletionModel through countTotalUsage');
3701
+ const promptResult = await target.callCompletionModel(prompt);
3702
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3703
+ spending.next(promptResult.usage);
3704
+ return promptResult;
3705
+ };
3706
+ }
3707
+ // Handle callEmbeddingModel method with usage counting
3708
+ if (prop === 'callEmbeddingModel' && target.callEmbeddingModel !== undefined) {
3709
+ return async (prompt) => {
3710
+ // console.info('[🚕] callEmbeddingModel through countTotalUsage');
3711
+ const promptResult = await target.callEmbeddingModel(prompt);
3712
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3713
+ spending.next(promptResult.usage);
3714
+ return promptResult;
3715
+ };
3716
+ }
3717
+ // Handle callImageGenerationModel method with usage counting
3718
+ if (prop === 'callImageGenerationModel' && target.callImageGenerationModel !== undefined) {
3719
+ return async (prompt) => {
3720
+ // console.info('[🚕] callImageGenerationModel through countTotalUsage');
3721
+ const promptResult = await target.callImageGenerationModel(prompt);
3722
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3723
+ spending.next(promptResult.usage);
3724
+ return promptResult;
3725
+ };
3726
+ }
3727
+ // <- Note: [🤖]
3728
+ // For all other properties and methods, delegate to the original target
3729
+ const value = Reflect.get(target, prop, receiver);
3730
+ // If it's a function, bind it to the target to preserve context
3731
+ if (typeof value === 'function') {
3732
+ return value.bind(target);
3733
+ }
3734
+ return value;
3681
3735
  },
3682
- };
3683
- if (llmTools.callChatModel !== undefined) {
3684
- proxyTools.callChatModel = async (prompt) => {
3685
- // console.info('[🚕] callChatModel through countTotalUsage');
3686
- const promptResult = await llmTools.callChatModel(prompt);
3687
- totalUsage = addUsage(totalUsage, promptResult.usage);
3688
- spending.next(promptResult.usage);
3689
- return promptResult;
3690
- };
3691
- }
3692
- if (llmTools.callCompletionModel !== undefined) {
3693
- proxyTools.callCompletionModel = async (prompt) => {
3694
- // console.info('[🚕] callCompletionModel through countTotalUsage');
3695
- const promptResult = await llmTools.callCompletionModel(prompt);
3696
- totalUsage = addUsage(totalUsage, promptResult.usage);
3697
- spending.next(promptResult.usage);
3698
- return promptResult;
3699
- };
3700
- }
3701
- if (llmTools.callEmbeddingModel !== undefined) {
3702
- proxyTools.callEmbeddingModel = async (prompt) => {
3703
- // console.info('[🚕] callEmbeddingModel through countTotalUsage');
3704
- const promptResult = await llmTools.callEmbeddingModel(prompt);
3705
- totalUsage = addUsage(totalUsage, promptResult.usage);
3706
- spending.next(promptResult.usage);
3707
- return promptResult;
3708
- };
3709
- }
3710
- if (llmTools.callImageGenerationModel !== undefined) {
3711
- proxyTools.callImageGenerationModel = async (prompt) => {
3712
- // console.info('[🚕] callImageGenerationModel through countTotalUsage');
3713
- const promptResult = await llmTools.callImageGenerationModel(prompt);
3714
- totalUsage = addUsage(totalUsage, promptResult.usage);
3715
- spending.next(promptResult.usage);
3716
- return promptResult;
3717
- };
3718
- }
3719
- // <- Note: [🤖]
3736
+ });
3720
3737
  return proxyTools;
3721
3738
  }
3722
3739
  /**
@@ -7494,6 +7511,40 @@ async function preparePersona(personaDescription, tools, options) {
7494
7511
  * TODO: [🏢] Check validity of `temperature` in pipeline
7495
7512
  */
7496
7513
 
7514
+ /**
7515
+ * Creates an empty/basic agent model requirements object
7516
+ * This serves as the starting point for the reduce-like pattern
7517
+ * where each commitment applies its changes to build the final requirements
7518
+ *
7519
+ * @public exported from `@promptbook/core`
7520
+ */
7521
+ function createEmptyAgentModelRequirements() {
7522
+ return {
7523
+ systemMessage: '',
7524
+ // modelName: 'gpt-5',
7525
+ modelName: 'gemini-2.5-flash-lite',
7526
+ temperature: 0.7,
7527
+ topP: 0.9,
7528
+ topK: 50,
7529
+ };
7530
+ }
7531
+ /**
7532
+ * Creates a basic agent model requirements with just the agent name
7533
+ * This is used when we have an agent name but no commitments
7534
+ *
7535
+ * @public exported from `@promptbook/core`
7536
+ */
7537
+ function createBasicAgentModelRequirements(agentName) {
7538
+ const empty = createEmptyAgentModelRequirements();
7539
+ return {
7540
+ ...empty,
7541
+ systemMessage: `You are ${agentName || 'AI Agent'}`,
7542
+ };
7543
+ }
7544
+ /**
7545
+ * TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
7546
+ */
7547
+
7497
7548
  /**
7498
7549
  * Generates a regex pattern to match a specific commitment
7499
7550
  *
@@ -7610,6 +7661,22 @@ class BaseCommitmentDefinition {
7610
7661
  return this.appendToSystemMessage(requirements, commentSection);
7611
7662
  }
7612
7663
  }
7664
+ /**
7665
+ * Gets tool function implementations provided by this commitment
7666
+ *
7667
+ * When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
7668
+ */
7669
+ getToolFunctions() {
7670
+ return {};
7671
+ }
7672
+ /**
7673
+ * Gets human-readable titles for tool functions provided by this commitment
7674
+ *
7675
+ * This is used in the UI to show a user-friendly name instead of the technical function name.
7676
+ */
7677
+ getToolTitles() {
7678
+ return {};
7679
+ }
7613
7680
  }
7614
7681
 
7615
7682
  /**
@@ -8466,6 +8533,698 @@ function $getCurrentDate() {
8466
8533
  return new Date().toISOString();
8467
8534
  }
8468
8535
 
8536
+ /**
8537
+ * Creates human-readable hash
8538
+ *
8539
+ * @public exported from `@promptbook/utils`
8540
+ */
8541
+ async function linguisticHash(input) {
8542
+ const hash = computeHash(input);
8543
+ // Use parts of the hash to select words
8544
+ // SHA256 is 64 hex characters
8545
+ // We use different slices of the hash to ensure variety even with small changes in input
8546
+ const part1 = parseInt(hash.substring(0, 10), 16);
8547
+ const part2 = parseInt(hash.substring(10, 20), 16);
8548
+ const part3 = parseInt(hash.substring(20, 30), 16);
8549
+ const adjective = ADJECTIVES[part1 % ADJECTIVES.length];
8550
+ const noun = NOUNS[part2 % NOUNS.length];
8551
+ const verb = VERBS[part3 % VERBS.length];
8552
+ return `${capitalize(adjective)} ${noun.toLowerCase()} ${verb.toLowerCase()}`;
8553
+ }
8554
+ const ADJECTIVES = [
8555
+ 'red',
8556
+ 'blue',
8557
+ 'green',
8558
+ 'yellow',
8559
+ 'quick',
8560
+ 'slow',
8561
+ 'bright',
8562
+ 'dark',
8563
+ 'happy',
8564
+ 'sad',
8565
+ 'brave',
8566
+ 'calm',
8567
+ 'clever',
8568
+ 'eager',
8569
+ 'fancy',
8570
+ 'grand',
8571
+ 'jolly',
8572
+ 'kind',
8573
+ 'lucky',
8574
+ 'nice',
8575
+ 'proud',
8576
+ 'silly',
8577
+ 'wise',
8578
+ 'young',
8579
+ 'old',
8580
+ 'big',
8581
+ 'small',
8582
+ 'fast',
8583
+ 'shiny',
8584
+ 'wild',
8585
+ 'silent',
8586
+ 'loud',
8587
+ 'soft',
8588
+ 'hard',
8589
+ 'warm',
8590
+ 'cold',
8591
+ 'sweet',
8592
+ 'sour',
8593
+ 'bitter',
8594
+ 'salty',
8595
+ 'rich',
8596
+ 'poor',
8597
+ 'heavy',
8598
+ 'light',
8599
+ 'strong',
8600
+ 'weak',
8601
+ 'smooth',
8602
+ 'rough',
8603
+ 'clean',
8604
+ 'dirty',
8605
+ 'fresh',
8606
+ 'stale',
8607
+ 'sharp',
8608
+ 'blunt',
8609
+ 'thick',
8610
+ 'thin',
8611
+ 'wide',
8612
+ 'narrow',
8613
+ 'deep',
8614
+ 'shallow',
8615
+ 'mighty',
8616
+ 'gentle',
8617
+ 'fierce',
8618
+ 'vibrant',
8619
+ 'dusty',
8620
+ 'golden',
8621
+ 'silver',
8622
+ 'frozen',
8623
+ 'burning',
8624
+ 'ancient',
8625
+ 'modern',
8626
+ 'hidden',
8627
+ 'lost',
8628
+ 'found',
8629
+ 'magic',
8630
+ 'mystic',
8631
+ 'cosmic',
8632
+ 'stellar',
8633
+ 'lunar',
8634
+ 'solar',
8635
+ 'misty',
8636
+ 'foggy',
8637
+ 'stormy',
8638
+ 'sunny',
8639
+ 'windy',
8640
+ 'quiet',
8641
+ 'noisy',
8642
+ 'peaceful',
8643
+ 'busy',
8644
+ 'empty',
8645
+ 'full',
8646
+ 'round',
8647
+ 'square',
8648
+ 'flat',
8649
+ 'curved',
8650
+ 'tiny',
8651
+ 'huge',
8652
+ 'giant',
8653
+ 'little',
8654
+ 'short',
8655
+ 'long',
8656
+ 'near',
8657
+ 'distant',
8658
+ 'inner',
8659
+ 'outer',
8660
+ 'patient',
8661
+ 'steady',
8662
+ 'noble',
8663
+ 'pure',
8664
+ 'graceful',
8665
+ 'honest',
8666
+ 'simple',
8667
+ 'complex',
8668
+ 'active',
8669
+ 'passive',
8670
+ 'vivid',
8671
+ 'pale',
8672
+ 'loyal',
8673
+ 'true',
8674
+ 'false',
8675
+ 'fair',
8676
+ 'clear',
8677
+ 'murky',
8678
+ 'vast',
8679
+ 'slick',
8680
+ 'slippery',
8681
+ 'sticky',
8682
+ 'dull',
8683
+ 'keen',
8684
+ 'broad',
8685
+ 'slim',
8686
+ 'slender',
8687
+ 'fat',
8688
+ 'lean',
8689
+ 'stiff',
8690
+ 'flexible',
8691
+ 'rigid',
8692
+ 'elastic',
8693
+ 'tough',
8694
+ 'brittle',
8695
+ 'fragile',
8696
+ 'solid',
8697
+ 'liquid',
8698
+ 'gaseous',
8699
+ 'airy',
8700
+ 'weighty',
8701
+ 'buoyant',
8702
+ 'dense',
8703
+ 'sparse',
8704
+ 'hollow',
8705
+ 'stuffed',
8706
+ 'crowded',
8707
+ 'lonely',
8708
+ 'social',
8709
+ 'private',
8710
+ 'public',
8711
+ 'secret',
8712
+ 'famous',
8713
+ 'certain',
8714
+ 'vague',
8715
+ 'plain',
8716
+ 'easy',
8717
+ 'tame',
8718
+ 'mild',
8719
+ 'hot',
8720
+ 'cool',
8721
+ 'dry',
8722
+ 'wet',
8723
+ 'damp',
8724
+ 'moist',
8725
+ 'soaked',
8726
+ 'parched',
8727
+ 'hungry',
8728
+ 'thirsty',
8729
+ 'sleepy',
8730
+ 'awake',
8731
+ 'tired',
8732
+ 'lazy',
8733
+ 'idle',
8734
+ 'swift',
8735
+ 'rapid',
8736
+ 'unstable',
8737
+ 'shaky',
8738
+ 'firm',
8739
+ 'bold',
8740
+ 'timid',
8741
+ 'brave',
8742
+ 'cowardly',
8743
+ 'smart',
8744
+ 'dumb',
8745
+ 'foolish',
8746
+ 'mean',
8747
+ 'rude',
8748
+ 'tasty',
8749
+ 'bland',
8750
+ 'arctic',
8751
+ 'tropical',
8752
+ 'deserted',
8753
+ 'urban',
8754
+ 'rural',
8755
+ 'local',
8756
+ 'global',
8757
+ 'digital',
8758
+ 'analog',
8759
+ 'virtual',
8760
+ 'real',
8761
+ 'fake',
8762
+ 'natural',
8763
+ 'artificial',
8764
+ 'living',
8765
+ 'dead',
8766
+ 'broken',
8767
+ 'fixed',
8768
+ 'new',
8769
+ 'worn',
8770
+ 'neat',
8771
+ 'messy',
8772
+ 'brave',
8773
+ 'fearful',
8774
+ 'proud',
8775
+ 'humble',
8776
+ 'greedy',
8777
+ 'generous',
8778
+ 'calm',
8779
+ 'angry',
8780
+ 'happy',
8781
+ 'sad',
8782
+ 'excited',
8783
+ 'bored',
8784
+ 'strange',
8785
+ 'normal',
8786
+ 'odd',
8787
+ 'even',
8788
+ 'rare',
8789
+ 'common',
8790
+ 'unique',
8791
+ 'plain',
8792
+ 'fancy',
8793
+ 'basic',
8794
+ 'prime',
8795
+ 'super',
8796
+ 'mega',
8797
+ 'ultra',
8798
+ 'micro',
8799
+ 'nano',
8800
+ 'macro',
8801
+ 'mini',
8802
+ ];
8803
+ const NOUNS = [
8804
+ 'apple',
8805
+ 'sky',
8806
+ 'tree',
8807
+ 'fox',
8808
+ 'cat',
8809
+ 'bird',
8810
+ 'dog',
8811
+ 'river',
8812
+ 'mountain',
8813
+ 'forest',
8814
+ 'ocean',
8815
+ 'star',
8816
+ 'moon',
8817
+ 'sun',
8818
+ 'cloud',
8819
+ 'flower',
8820
+ 'leaf',
8821
+ 'stone',
8822
+ 'wind',
8823
+ 'rain',
8824
+ 'fire',
8825
+ 'ice',
8826
+ 'book',
8827
+ 'dream',
8828
+ 'song',
8829
+ 'road',
8830
+ 'gate',
8831
+ 'key',
8832
+ 'lamp',
8833
+ 'map',
8834
+ 'house',
8835
+ 'city',
8836
+ 'bridge',
8837
+ 'field',
8838
+ 'garden',
8839
+ 'lake',
8840
+ 'beach',
8841
+ 'island',
8842
+ 'valley',
8843
+ 'desert',
8844
+ 'world',
8845
+ 'spirit',
8846
+ 'heart',
8847
+ 'mind',
8848
+ 'soul',
8849
+ 'life',
8850
+ 'time',
8851
+ 'space',
8852
+ 'light',
8853
+ 'shadow',
8854
+ 'sound',
8855
+ 'music',
8856
+ 'voice',
8857
+ 'word',
8858
+ 'page',
8859
+ 'story',
8860
+ 'pearl',
8861
+ 'gold',
8862
+ 'silver',
8863
+ 'crystal',
8864
+ 'diamond',
8865
+ 'emerald',
8866
+ 'ruby',
8867
+ 'path',
8868
+ 'trail',
8869
+ 'peak',
8870
+ 'shore',
8871
+ 'wave',
8872
+ 'tide',
8873
+ 'flame',
8874
+ 'spark',
8875
+ 'beam',
8876
+ 'ray',
8877
+ 'seed',
8878
+ 'root',
8879
+ 'branch',
8880
+ 'bloom',
8881
+ 'thorn',
8882
+ 'bark',
8883
+ 'shell',
8884
+ 'feather',
8885
+ 'wing',
8886
+ 'claw',
8887
+ 'paw',
8888
+ 'nest',
8889
+ 'cave',
8890
+ 'grove',
8891
+ 'tower',
8892
+ 'castle',
8893
+ 'crown',
8894
+ 'sword',
8895
+ 'shield',
8896
+ 'coin',
8897
+ 'gem',
8898
+ 'ring',
8899
+ 'bell',
8900
+ 'clock',
8901
+ 'compass',
8902
+ 'anchor',
8903
+ 'torch',
8904
+ 'flute',
8905
+ 'harp',
8906
+ 'drum',
8907
+ 'lens',
8908
+ 'glass',
8909
+ 'sand',
8910
+ 'dust',
8911
+ 'mist',
8912
+ 'dew',
8913
+ 'dawn',
8914
+ 'dusk',
8915
+ 'night',
8916
+ 'day',
8917
+ 'year',
8918
+ 'age',
8919
+ 'bolt',
8920
+ 'drop',
8921
+ 'storm',
8922
+ 'snow',
8923
+ 'hail',
8924
+ 'fog',
8925
+ 'smoke',
8926
+ 'vapor',
8927
+ 'gas',
8928
+ 'fluid',
8929
+ 'liquid',
8930
+ 'solid',
8931
+ 'metal',
8932
+ 'rock',
8933
+ 'dirt',
8934
+ 'clay',
8935
+ 'sand',
8936
+ 'salt',
8937
+ 'sugar',
8938
+ 'wood',
8939
+ 'bone',
8940
+ 'skin',
8941
+ 'flesh',
8942
+ 'blood',
8943
+ 'cell',
8944
+ 'atom',
8945
+ 'pulse',
8946
+ 'beat',
8947
+ 'breath',
8948
+ 'sigh',
8949
+ 'name',
8950
+ 'echo',
8951
+ 'image',
8952
+ 'vision',
8953
+ 'thought',
8954
+ 'idea',
8955
+ 'plan',
8956
+ 'goal',
8957
+ 'wish',
8958
+ 'hope',
8959
+ 'fear',
8960
+ 'joy',
8961
+ 'love',
8962
+ 'hate',
8963
+ 'will',
8964
+ 'power',
8965
+ 'force',
8966
+ 'energy',
8967
+ 'motion',
8968
+ 'speed',
8969
+ 'place',
8970
+ 'point',
8971
+ 'line',
8972
+ 'shape',
8973
+ 'form',
8974
+ 'size',
8975
+ 'mass',
8976
+ 'weight',
8977
+ 'heat',
8978
+ 'cold',
8979
+ 'color',
8980
+ 'tone',
8981
+ 'pitch',
8982
+ 'rhythm',
8983
+ 'vibe',
8984
+ 'mood',
8985
+ 'state',
8986
+ 'way',
8987
+ 'step',
8988
+ 'move',
8989
+ 'turn',
8990
+ 'fall',
8991
+ 'rise',
8992
+ 'jump',
8993
+ 'leap',
8994
+ 'run',
8995
+ 'walk',
8996
+ 'rest',
8997
+ 'stay',
8998
+ 'trip',
8999
+ 'quest',
9000
+ 'task',
9001
+ 'work',
9002
+ 'job',
9003
+ 'play',
9004
+ 'game',
9005
+ 'sport',
9006
+ 'art',
9007
+ 'craft',
9008
+ 'tool',
9009
+ 'ship',
9010
+ 'boat',
9011
+ 'car',
9012
+ 'bike',
9013
+ 'train',
9014
+ 'plane',
9015
+ 'hub',
9016
+ 'base',
9017
+ 'core',
9018
+ 'node',
9019
+ 'link',
9020
+ 'net',
9021
+ 'web',
9022
+ 'box',
9023
+ 'bag',
9024
+ 'jar',
9025
+ 'cup',
9026
+ 'bowl',
9027
+ 'plate',
9028
+ 'dish',
9029
+ 'spoon',
9030
+ 'fork',
9031
+ 'knife',
9032
+ 'pan',
9033
+ 'pot',
9034
+ 'bed',
9035
+ 'desk',
9036
+ 'chair',
9037
+ 'door',
9038
+ 'wall',
9039
+ 'roof',
9040
+ 'floor',
9041
+ ];
9042
+ const VERBS = [
9043
+ 'jumping',
9044
+ 'dancing',
9045
+ 'flying',
9046
+ 'running',
9047
+ 'singing',
9048
+ 'shining',
9049
+ 'growing',
9050
+ 'flowing',
9051
+ 'falling',
9052
+ 'rising',
9053
+ 'sleeping',
9054
+ 'walking',
9055
+ 'talking',
9056
+ 'thinking',
9057
+ 'dreaming',
9058
+ 'looking',
9059
+ 'feeling',
9060
+ 'smiling',
9061
+ 'laughing',
9062
+ 'playing',
9063
+ 'working',
9064
+ 'resting',
9065
+ 'moving',
9066
+ 'staying',
9067
+ 'beaming',
9068
+ 'glowing',
9069
+ 'sparkling',
9070
+ 'waiting',
9071
+ 'waking',
9072
+ 'drifting',
9073
+ 'spinning',
9074
+ 'gliding',
9075
+ 'soaring',
9076
+ 'floating',
9077
+ 'whispering',
9078
+ 'calling',
9079
+ 'seeking',
9080
+ 'finding',
9081
+ 'giving',
9082
+ 'taking',
9083
+ 'weaving',
9084
+ 'building',
9085
+ 'creating',
9086
+ 'burning',
9087
+ 'freezing',
9088
+ 'melting',
9089
+ 'breathing',
9090
+ 'pulsing',
9091
+ 'beating',
9092
+ 'living',
9093
+ 'learning',
9094
+ 'knowing',
9095
+ 'hidden',
9096
+ 'shown',
9097
+ 'broken',
9098
+ 'mended',
9099
+ 'lost',
9100
+ 'found',
9101
+ 'starting',
9102
+ 'ending',
9103
+ 'climbing',
9104
+ 'diving',
9105
+ 'swimming',
9106
+ 'sailing',
9107
+ 'rolling',
9108
+ 'shaking',
9109
+ 'turning',
9110
+ 'shifting',
9111
+ 'changing',
9112
+ 'fading',
9113
+ 'dying',
9114
+ 'born',
9115
+ 'humming',
9116
+ 'crying',
9117
+ 'racing',
9118
+ 'creeping',
9119
+ 'hiding',
9120
+ 'watching',
9121
+ 'hearing',
9122
+ 'sensing',
9123
+ 'longing',
9124
+ 'hoping',
9125
+ 'loving',
9126
+ 'fearing',
9127
+ 'wondering',
9128
+ 'wandering',
9129
+ 'traveling',
9130
+ 'crossing',
9131
+ 'meeting',
9132
+ 'parting',
9133
+ 'keeping',
9134
+ 'sharing',
9135
+ 'sparking',
9136
+ 'flaming',
9137
+ 'healing',
9138
+ 'solving',
9139
+ 'opening',
9140
+ 'closing',
9141
+ 'lifting',
9142
+ 'pulling',
9143
+ 'pushing',
9144
+ 'holding',
9145
+ 'tossing',
9146
+ 'throwing',
9147
+ 'catching',
9148
+ 'fixing',
9149
+ 'making',
9150
+ 'doing',
9151
+ 'seeing',
9152
+ 'tasting',
9153
+ 'smelling',
9154
+ 'touching',
9155
+ 'pacing',
9156
+ 'hurrying',
9157
+ 'pausing',
9158
+ 'going',
9159
+ 'coming',
9160
+ 'leaving',
9161
+ 'acting',
9162
+ 'being',
9163
+ 'seeming',
9164
+ 'shrinking',
9165
+ 'widening',
9166
+ 'narrowing',
9167
+ 'heating',
9168
+ 'cooling',
9169
+ 'drying',
9170
+ 'wetting',
9171
+ 'filling',
9172
+ 'filling',
9173
+ 'emptying',
9174
+ 'letting',
9175
+ 'gaining',
9176
+ 'winning',
9177
+ 'failing',
9178
+ 'trying',
9179
+ 'using',
9180
+ 'getting',
9181
+ 'showing',
9182
+ 'hiding',
9183
+ 'breaking',
9184
+ 'fixing',
9185
+ 'saving',
9186
+ 'spending',
9187
+ 'buying',
9188
+ 'selling',
9189
+ 'paying',
9190
+ 'costing',
9191
+ 'reaching',
9192
+ 'missing',
9193
+ 'hitting',
9194
+ 'striking',
9195
+ 'leading',
9196
+ 'following',
9197
+ 'helping',
9198
+ 'serving',
9199
+ 'teaching',
9200
+ 'training',
9201
+ 'coding',
9202
+ 'writing',
9203
+ 'reading',
9204
+ 'drawing',
9205
+ 'painting',
9206
+ 'crafting',
9207
+ 'shaping',
9208
+ 'forming',
9209
+ 'joining',
9210
+ 'splitting',
9211
+ 'sharing',
9212
+ 'bonding',
9213
+ 'healing',
9214
+ 'harming',
9215
+ 'protecting',
9216
+ 'fighting',
9217
+ 'defending',
9218
+ 'attacking',
9219
+ 'escaping',
9220
+ 'catching',
9221
+ 'trapping',
9222
+ 'freeing',
9223
+ 'binding',
9224
+ 'weaving',
9225
+ 'spinning',
9226
+ ];
9227
+
8469
9228
  /**
8470
9229
  * Function parseNumber will parse number from string
8471
9230
  *
@@ -8574,6 +9333,95 @@ function removeQuotes(text) {
8574
9333
  return text;
8575
9334
  }
8576
9335
 
9336
+ /**
9337
+ * Removes quotes and optional introduce text from a string
9338
+ *
9339
+ * Tip: This is very useful for post-processing of the result of the LLM model
9340
+ * Note: This function trims the text and removes whole introduce sentence if it is present
9341
+ * Note: There are two similar functions:
9342
+ * - `removeQuotes` which removes only bounding quotes
9343
+ * - `unwrapResult` which removes whole introduce sentence
9344
+ *
9345
+ * @param text optionally quoted text
9346
+ * @returns text without quotes
9347
+ * @public exported from `@promptbook/utils`
9348
+ */
9349
+ function unwrapResult(text, options) {
9350
+ const { isTrimmed = true, isIntroduceSentenceRemoved = true } = options || {};
9351
+ let trimmedText = text;
9352
+ // Remove leading and trailing spaces and newlines
9353
+ if (isTrimmed) {
9354
+ trimmedText = spaceTrim$1(trimmedText);
9355
+ }
9356
+ let processedText = trimmedText;
9357
+ // Check for markdown code block
9358
+ const codeBlockRegex = /^```[a-z]*\n([\s\S]*?)\n```\s*$/;
9359
+ const codeBlockMatch = processedText.match(codeBlockRegex);
9360
+ if (codeBlockMatch && codeBlockMatch[1] !== undefined) {
9361
+ // Check if there's only one code block
9362
+ const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
9363
+ if (codeBlockCount === 1) {
9364
+ return unwrapResult(codeBlockMatch[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
9365
+ }
9366
+ }
9367
+ if (isIntroduceSentenceRemoved) {
9368
+ const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
9369
+ if (introduceSentenceRegex.test(text)) {
9370
+ // Remove the introduce sentence and quotes by replacing it with an empty string
9371
+ processedText = processedText.replace(introduceSentenceRegex, '');
9372
+ }
9373
+ processedText = spaceTrim$1(processedText);
9374
+ // Check again for code block after removing introduce sentence
9375
+ const codeBlockMatch2 = processedText.match(codeBlockRegex);
9376
+ if (codeBlockMatch2 && codeBlockMatch2[1] !== undefined) {
9377
+ const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
9378
+ if (codeBlockCount === 1) {
9379
+ return unwrapResult(codeBlockMatch2[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
9380
+ }
9381
+ }
9382
+ }
9383
+ if (processedText.length < 3) {
9384
+ return trimmedText;
9385
+ }
9386
+ if (processedText.includes('\n')) {
9387
+ return trimmedText;
9388
+ }
9389
+ // Remove the quotes by extracting the substring without the first and last characters
9390
+ const unquotedText = processedText.slice(1, -1);
9391
+ // Check if the text starts and ends with quotes
9392
+ if ([
9393
+ ['"', '"'],
9394
+ ["'", "'"],
9395
+ ['`', '`'],
9396
+ ['*', '*'],
9397
+ ['_', '_'],
9398
+ ['„', '“'],
9399
+ ['«', '»'] /* <- QUOTES to config */,
9400
+ ].some(([startQuote, endQuote]) => {
9401
+ if (!processedText.startsWith(startQuote)) {
9402
+ return false;
9403
+ }
9404
+ if (!processedText.endsWith(endQuote)) {
9405
+ return false;
9406
+ }
9407
+ if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
9408
+ return false;
9409
+ }
9410
+ if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
9411
+ return false;
9412
+ }
9413
+ return true;
9414
+ })) {
9415
+ return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
9416
+ }
9417
+ else {
9418
+ return processedText;
9419
+ }
9420
+ }
9421
+ /**
9422
+ * TODO: [🧠] Should this also unwrap the (parenthesis)
9423
+ */
9424
+
8577
9425
  /**
8578
9426
  * Checks if the given value is a valid JavaScript identifier name.
8579
9427
  *
@@ -8708,79 +9556,6 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
8708
9556
  * Note: [💞] Ignore a discrepancy between file name and entity name
8709
9557
  */
8710
9558
 
8711
- /**
8712
- * IMPORT commitment definition
8713
- *
8714
- * The IMPORT commitment tells the agent to import content from another agent at the current location.
8715
- *
8716
- * Example usage in agent source:
8717
- *
8718
- * ```book
8719
- * IMPORT https://s6.ptbk.io/benjamin-white
8720
- * ```
8721
- *
8722
- * @private [🪔] Maybe export the commitments through some package
8723
- */
8724
- class ImportCommitmentDefinition extends BaseCommitmentDefinition {
8725
- constructor(type = 'IMPORT') {
8726
- super(type);
8727
- }
8728
- /**
8729
- * Short one-line description of IMPORT.
8730
- */
8731
- get description() {
8732
- return 'Import content from another agent.';
8733
- }
8734
- /**
8735
- * Icon for this commitment.
8736
- */
8737
- get icon() {
8738
- return '📥';
8739
- }
8740
- /**
8741
- * Markdown documentation for IMPORT commitment.
8742
- */
8743
- get documentation() {
8744
- return spaceTrim$1(`
8745
- # ${this.type}
8746
-
8747
- Imports content from another agent at the location of the commitment.
8748
-
8749
- ## Examples
8750
-
8751
- \`\`\`book
8752
- My AI Agent
8753
-
8754
- IMPORT https://s6.ptbk.io/benjamin-white
8755
- RULE Speak only in English.
8756
- \`\`\`
8757
- `);
8758
- }
8759
- applyToAgentModelRequirements(requirements, content) {
8760
- const trimmedContent = content.trim();
8761
- if (!trimmedContent) {
8762
- return requirements;
8763
- }
8764
- if (!isValidAgentUrl(trimmedContent)) {
8765
- throw new Error(spaceTrim$1((block) => `
8766
- Invalid agent URL in IMPORT commitment: "${trimmedContent}"
8767
-
8768
- \`\`\`book
8769
- ${block(content)}
8770
- \`\`\`
8771
- `));
8772
- }
8773
- const importedAgentUrl = trimmedContent;
8774
- return {
8775
- ...requirements,
8776
- importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
8777
- };
8778
- }
8779
- }
8780
- /**
8781
- * Note: [💞] Ignore a discrepancy between file name and entity name
8782
- */
8783
-
8784
9559
  /**
8785
9560
  * GOAL commitment definition
8786
9561
  *
@@ -8882,80 +9657,52 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
8882
9657
  */
8883
9658
 
8884
9659
  /**
8885
- * KNOWLEDGE commitment definition
8886
- *
8887
- * The KNOWLEDGE commitment adds specific knowledge, facts, or context to the agent
8888
- * using RAG (Retrieval-Augmented Generation) approach for external sources.
9660
+ * IMPORT commitment definition
8889
9661
  *
8890
- * Supports both direct text knowledge and external sources like PDFs.
9662
+ * The IMPORT commitment tells the agent to import content from another agent at the current location.
8891
9663
  *
8892
9664
  * Example usage in agent source:
8893
9665
  *
8894
9666
  * ```book
8895
- * KNOWLEDGE The company was founded in 2020 and specializes in AI-powered solutions
8896
- * KNOWLEDGE https://example.com/company-handbook.pdf
8897
- * KNOWLEDGE https://example.com/product-documentation.pdf
9667
+ * IMPORT https://s6.ptbk.io/benjamin-white
8898
9668
  * ```
8899
9669
  *
8900
9670
  * @private [🪔] Maybe export the commitments through some package
8901
9671
  */
8902
- class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
8903
- constructor() {
8904
- super('KNOWLEDGE');
9672
+ class ImportCommitmentDefinition extends BaseCommitmentDefinition {
9673
+ constructor(type = 'IMPORT') {
9674
+ super(type);
8905
9675
  }
8906
9676
  /**
8907
- * Short one-line description of KNOWLEDGE.
9677
+ * Short one-line description of IMPORT.
8908
9678
  */
8909
9679
  get description() {
8910
- return 'Add domain **knowledge** via direct text or external sources (RAG).';
9680
+ return 'Import content from another agent or a generic text file.';
8911
9681
  }
8912
9682
  /**
8913
9683
  * Icon for this commitment.
8914
9684
  */
8915
9685
  get icon() {
8916
- return '🧠';
9686
+ return '📥';
8917
9687
  }
8918
9688
  /**
8919
- * Markdown documentation for KNOWLEDGE commitment.
9689
+ * Markdown documentation for IMPORT commitment.
8920
9690
  */
8921
9691
  get documentation() {
8922
9692
  return spaceTrim$1(`
8923
9693
  # ${this.type}
8924
9694
 
8925
- Adds specific knowledge, facts, or context to the agent using a RAG (Retrieval-Augmented Generation) approach for external sources.
8926
-
8927
- ## Key aspects
8928
-
8929
- - Both terms work identically and can be used interchangeably.
8930
- - Supports both direct text knowledge and external URLs.
8931
- - External sources (PDFs, websites) are processed via RAG for context retrieval.
8932
-
8933
- ## Supported formats
8934
-
8935
- - Direct text: Immediate knowledge incorporated into agent
8936
- - URLs: External documents processed for contextual retrieval
8937
- - Supported file types: PDF, text, markdown, HTML
9695
+ Imports content from another agent or a generic text file at the location of the commitment.
8938
9696
 
8939
9697
  ## Examples
8940
9698
 
8941
9699
  \`\`\`book
8942
- Customer Support Bot
8943
-
8944
- PERSONA You are a helpful customer support agent for TechCorp
8945
- KNOWLEDGE TechCorp was founded in 2020 and specializes in AI-powered solutions
8946
- KNOWLEDGE https://example.com/company-handbook.pdf
8947
- KNOWLEDGE https://example.com/product-documentation.pdf
8948
- RULE Always be polite and professional
8949
- \`\`\`
8950
-
8951
- \`\`\`book
8952
- Research Assistant
9700
+ My AI Agent
8953
9701
 
8954
- PERSONA You are a knowledgeable research assistant
8955
- KNOWLEDGE Academic research requires careful citation and verification
8956
- KNOWLEDGE https://example.com/research-guidelines.pdf
8957
- ACTION Can help with literature reviews and data analysis
8958
- STYLE Present information in clear, academic format
9702
+ IMPORT https://s6.ptbk.io/benjamin-white
9703
+ IMPORT https://example.com/some-text-file.txt
9704
+ IMPORT ./path/to/local-file.json
9705
+ RULE Speak only in English.
8959
9706
  \`\`\`
8960
9707
  `);
8961
9708
  }
@@ -8964,13 +9711,122 @@ class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
8964
9711
  if (!trimmedContent) {
8965
9712
  return requirements;
8966
9713
  }
8967
- // Check if content is a URL (external knowledge source)
8968
- if (isValidUrl(trimmedContent)) {
8969
- // Store the URL for later async processing
8970
- const updatedRequirements = {
9714
+ if (isValidAgentUrl(trimmedContent)) {
9715
+ const importedAgentUrl = trimmedContent;
9716
+ return {
8971
9717
  ...requirements,
8972
- knowledgeSources: [
8973
- ...(requirements.knowledgeSources || []),
9718
+ importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
9719
+ };
9720
+ }
9721
+ if (isValidUrl(trimmedContent) || isValidFilePath(trimmedContent)) {
9722
+ return {
9723
+ ...requirements,
9724
+ importedFileUrls: [...(requirements.importedFileUrls || []), trimmedContent],
9725
+ };
9726
+ }
9727
+ throw new Error(spaceTrim$1((block) => `
9728
+ Invalid agent URL or file path in IMPORT commitment: "${trimmedContent}"
9729
+
9730
+ \`\`\`book
9731
+ ${block(content)}
9732
+ \`\`\`
9733
+ `));
9734
+ }
9735
+ }
9736
+ /**
9737
+ * Note: [💞] Ignore a discrepancy between file name and entity name
9738
+ */
9739
+
9740
+ /**
9741
+ * KNOWLEDGE commitment definition
9742
+ *
9743
+ * The KNOWLEDGE commitment adds specific knowledge, facts, or context to the agent
9744
+ * using RAG (Retrieval-Augmented Generation) approach for external sources.
9745
+ *
9746
+ * Supports both direct text knowledge and external sources like PDFs.
9747
+ *
9748
+ * Example usage in agent source:
9749
+ *
9750
+ * ```book
9751
+ * KNOWLEDGE The company was founded in 2020 and specializes in AI-powered solutions
9752
+ * KNOWLEDGE https://example.com/company-handbook.pdf
9753
+ * KNOWLEDGE https://example.com/product-documentation.pdf
9754
+ * ```
9755
+ *
9756
+ * @private [🪔] Maybe export the commitments through some package
9757
+ */
9758
+ class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
9759
+ constructor() {
9760
+ super('KNOWLEDGE');
9761
+ }
9762
+ /**
9763
+ * Short one-line description of KNOWLEDGE.
9764
+ */
9765
+ get description() {
9766
+ return 'Add domain **knowledge** via direct text or external sources (RAG).';
9767
+ }
9768
+ /**
9769
+ * Icon for this commitment.
9770
+ */
9771
+ get icon() {
9772
+ return '🧠';
9773
+ }
9774
+ /**
9775
+ * Markdown documentation for KNOWLEDGE commitment.
9776
+ */
9777
+ get documentation() {
9778
+ return spaceTrim$1(`
9779
+ # ${this.type}
9780
+
9781
+ Adds specific knowledge, facts, or context to the agent using a RAG (Retrieval-Augmented Generation) approach for external sources.
9782
+
9783
+ ## Key aspects
9784
+
9785
+ - Both terms work identically and can be used interchangeably.
9786
+ - Supports both direct text knowledge and external URLs.
9787
+ - External sources (PDFs, websites) are processed via RAG for context retrieval.
9788
+
9789
+ ## Supported formats
9790
+
9791
+ - Direct text: Immediate knowledge incorporated into agent
9792
+ - URLs: External documents processed for contextual retrieval
9793
+ - Supported file types: PDF, text, markdown, HTML
9794
+
9795
+ ## Examples
9796
+
9797
+ \`\`\`book
9798
+ Customer Support Bot
9799
+
9800
+ PERSONA You are a helpful customer support agent for TechCorp
9801
+ KNOWLEDGE TechCorp was founded in 2020 and specializes in AI-powered solutions
9802
+ KNOWLEDGE https://example.com/company-handbook.pdf
9803
+ KNOWLEDGE https://example.com/product-documentation.pdf
9804
+ RULE Always be polite and professional
9805
+ \`\`\`
9806
+
9807
+ \`\`\`book
9808
+ Research Assistant
9809
+
9810
+ PERSONA You are a knowledgeable research assistant
9811
+ KNOWLEDGE Academic research requires careful citation and verification
9812
+ KNOWLEDGE https://example.com/research-guidelines.pdf
9813
+ ACTION Can help with literature reviews and data analysis
9814
+ STYLE Present information in clear, academic format
9815
+ \`\`\`
9816
+ `);
9817
+ }
9818
+ applyToAgentModelRequirements(requirements, content) {
9819
+ const trimmedContent = content.trim();
9820
+ if (!trimmedContent) {
9821
+ return requirements;
9822
+ }
9823
+ // Check if content is a URL (external knowledge source)
9824
+ if (isValidUrl(trimmedContent)) {
9825
+ // Store the URL for later async processing
9826
+ const updatedRequirements = {
9827
+ ...requirements,
9828
+ knowledgeSources: [
9829
+ ...(requirements.knowledgeSources || []),
8974
9830
  trimmedContent,
8975
9831
  ],
8976
9832
  };
@@ -9300,7 +10156,13 @@ class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
9300
10156
  `);
9301
10157
  }
9302
10158
  applyToAgentModelRequirements(requirements, content) {
9303
- return requirements;
10159
+ // INITIAL MESSAGE is for UI display purposes and for conversation history construction.
10160
+ const newSample = { question: null, answer: content };
10161
+ const newSamples = [...(requirements.samples || []), newSample];
10162
+ return {
10163
+ ...requirements,
10164
+ samples: newSamples,
10165
+ };
9304
10166
  }
9305
10167
  }
9306
10168
 
@@ -10332,27 +11194,16 @@ class NoteCommitmentDefinition extends BaseCommitmentDefinition {
10332
11194
  `);
10333
11195
  }
10334
11196
  applyToAgentModelRequirements(requirements, content) {
10335
- var _a;
10336
11197
  // The NOTE commitment makes no changes to the system message or model requirements
10337
11198
  // It only stores the note content in metadata for documentation purposes
10338
- const trimmedContent = content.trim();
10339
- if (!trimmedContent) {
11199
+ const trimmedContent = spaceTrim$1(content);
11200
+ if (trimmedContent === '') {
10340
11201
  return requirements;
10341
11202
  }
10342
- // Get existing note content from metadata
10343
- const existingNoteContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.NOTE) || '';
10344
- // Merge the new content with existing note content
10345
- // When multiple NOTE commitments exist, they are aggregated together
10346
- const mergedNoteContent = existingNoteContent ? `${existingNoteContent}\n${trimmedContent}` : trimmedContent;
10347
- // Store the merged note content in metadata for debugging and inspection
10348
- const updatedMetadata = {
10349
- ...requirements.metadata,
10350
- NOTE: mergedNoteContent,
10351
- };
10352
- // Return requirements with updated metadata but no changes to system message
11203
+ // Return requirements with updated notes but no changes to system message
10353
11204
  return {
10354
11205
  ...requirements,
10355
- metadata: updatedMetadata,
11206
+ notes: [...(requirements.notes || []), trimmedContent],
10356
11207
  };
10357
11208
  }
10358
11209
  }
@@ -11136,6 +11987,14 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
11136
11987
  \`\`\`
11137
11988
  `);
11138
11989
  }
11990
+ /**
11991
+ * Gets human-readable titles for tool functions provided by this commitment.
11992
+ */
11993
+ getToolTitles() {
11994
+ return {
11995
+ web_browser: 'Web browser',
11996
+ };
11997
+ }
11139
11998
  applyToAgentModelRequirements(requirements, content) {
11140
11999
  // Get existing tools array or create new one
11141
12000
  const existingTools = requirements.tools || [];
@@ -11164,14 +12023,17 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
11164
12023
  },
11165
12024
  ]);
11166
12025
  // Return requirements with updated tools and metadata
11167
- return {
12026
+ return this.appendToSystemMessage({
11168
12027
  ...requirements,
11169
12028
  tools: updatedTools,
11170
12029
  metadata: {
11171
12030
  ...requirements.metadata,
11172
12031
  useBrowser: true,
11173
12032
  },
11174
- };
12033
+ }, spaceTrim$1(`
12034
+ You have access to the web browser. Use it to access specific websites or browse the internet.
12035
+ When you need to know some information from a specific website, use the tool provided to you.
12036
+ `));
11175
12037
  }
11176
12038
  }
11177
12039
  /**
@@ -11255,6 +12117,49 @@ class UseMcpCommitmentDefinition extends BaseCommitmentDefinition {
11255
12117
  * Note: [💞] Ignore a discrepancy between file name and entity name
11256
12118
  */
11257
12119
 
12120
+ /**
12121
+ * A search engine implementation that uses the SerpApi to fetch Google search results.
12122
+ *
12123
+ * @private <- TODO: !!!! Export via some package
12124
+ */
12125
+ class SerpSearchEngine {
12126
+ get title() {
12127
+ return 'SerpApi Search Engine';
12128
+ }
12129
+ get description() {
12130
+ return 'Search engine that uses SerpApi to fetch Google search results';
12131
+ }
12132
+ checkConfiguration() {
12133
+ if (!process.env.SERP_API_KEY) {
12134
+ throw new Error('SERP_API_KEY is not configured');
12135
+ }
12136
+ }
12137
+ async search(query, options = {}) {
12138
+ const apiKey = process.env.SERP_API_KEY;
12139
+ if (!apiKey) {
12140
+ throw new Error('SERP_API_KEY is not configured');
12141
+ }
12142
+ const url = new URL('https://serpapi.com/search');
12143
+ url.searchParams.set('api_key', apiKey);
12144
+ url.searchParams.set('engine', 'google');
12145
+ url.searchParams.set('q', query);
12146
+ for (const [key, value] of Object.entries(options)) {
12147
+ url.searchParams.set(key, String(value));
12148
+ }
12149
+ const response = await fetch(url.toString());
12150
+ if (!response.ok) {
12151
+ const body = await response.text();
12152
+ throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
12153
+ }
12154
+ const data = (await response.json());
12155
+ return (data.organic_results || []).map((item) => ({
12156
+ title: item.title,
12157
+ url: item.link,
12158
+ snippet: item.snippet || '',
12159
+ }));
12160
+ }
12161
+ }
12162
+
11258
12163
  /**
11259
12164
  * USE SEARCH ENGINE commitment definition
11260
12165
  *
@@ -11274,7 +12179,7 @@ class UseMcpCommitmentDefinition extends BaseCommitmentDefinition {
11274
12179
  */
11275
12180
  class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
11276
12181
  constructor() {
11277
- super('USE SEARCH ENGINE', ['SEARCH ENGINE', 'SEARCH']);
12182
+ super('USE SEARCH ENGINE', ['USE SEARCH']);
11278
12183
  }
11279
12184
  /**
11280
12185
  * Short one-line description of USE SEARCH ENGINE.
@@ -11331,18 +12236,13 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
11331
12236
  ? existingTools
11332
12237
  : [
11333
12238
  ...existingTools,
11334
- { type: 'web_search' },
11335
- // <- Note: [🔰] This is just using simple native search tool by OpenAI @see https://platform.openai.com/docs/guides/tools-web-search
11336
- // In future we will use proper MCP search tool:
11337
- /*
11338
-
11339
12239
  {
11340
12240
  name: 'web_search',
11341
- description: spaceTrim(`
11342
- Search the internet for information.
11343
- Use this tool when you need to find up-to-date information or facts that you don't know.
11344
- ${!content ? '' : `Search scope / instructions: ${content}`}
11345
- `),
12241
+ description: spaceTrim$1(`
12242
+ Search the internet for information.
12243
+ Use this tool when you need to find up-to-date information or facts that you don't know.
12244
+ ${!content ? '' : `Search scope / instructions: ${content}`}
12245
+ `),
11346
12246
  parameters: {
11347
12247
  type: 'object',
11348
12248
  properties: {
@@ -11350,20 +12250,224 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
11350
12250
  type: 'string',
11351
12251
  description: 'The search query',
11352
12252
  },
12253
+ location: {
12254
+ type: 'string',
12255
+ description: 'The location for the search (e.g., "Austin, Texas, United States" or "Prague, Czechia")',
12256
+ },
12257
+ gl: {
12258
+ type: 'string',
12259
+ description: 'The country code (e.g., "us" for United States, "cz" for Czechia)',
12260
+ },
12261
+ hl: {
12262
+ type: 'string',
12263
+ description: 'The language code (e.g., "en" for English, "cs" for Czech)',
12264
+ },
12265
+ num: {
12266
+ type: 'integer',
12267
+ description: 'Number of results to return',
12268
+ },
12269
+ engine: {
12270
+ type: 'string',
12271
+ description: 'The search engine to use (e.g., "google", "bing", "yahoo", "baidu")',
12272
+ },
12273
+ google_domain: {
12274
+ type: 'string',
12275
+ description: 'The Google domain to use (e.g., "google.com", "google.cz")',
12276
+ },
11353
12277
  },
11354
12278
  required: ['query'],
11355
12279
  },
11356
12280
  },
11357
- */
11358
12281
  ];
11359
12282
  // Return requirements with updated tools and metadata
11360
- return {
12283
+ return this.appendToSystemMessage({
11361
12284
  ...requirements,
11362
12285
  tools: updatedTools,
11363
12286
  metadata: {
11364
12287
  ...requirements.metadata,
11365
12288
  useSearchEngine: content || true,
11366
12289
  },
12290
+ }, spaceTrim$1(`
12291
+ You have access to the web search engine. Use it to find up-to-date information or facts that you don't know.
12292
+ When you need to know some information from the internet, use the tool provided to you.
12293
+ `));
12294
+ }
12295
+ /**
12296
+ * Gets human-readable titles for tool functions provided by this commitment.
12297
+ */
12298
+ getToolTitles() {
12299
+ return {
12300
+ web_search: 'Web search',
12301
+ };
12302
+ }
12303
+ /**
12304
+ * Gets the `web_search` tool function implementation.
12305
+ */
12306
+ getToolFunctions() {
12307
+ return {
12308
+ async web_search(args) {
12309
+ console.log('!!!! [Tool] web_search called', { args });
12310
+ const { query, ...options } = args;
12311
+ if (!query) {
12312
+ throw new Error('Search query is required');
12313
+ }
12314
+ const searchEngine = new SerpSearchEngine();
12315
+ const results = await searchEngine.search(query, options);
12316
+ return spaceTrim$1((block) => `
12317
+ Search results for "${query}"${Object.keys(options).length === 0
12318
+ ? ''
12319
+ : ` with options ${JSON.stringify(options)}`}:
12320
+
12321
+ ${block(results
12322
+ .map((result) => spaceTrim$1(`
12323
+ - **${result.title}**
12324
+ ${result.url}
12325
+ ${result.snippet}
12326
+ `))
12327
+ .join('\n\n'))}
12328
+ `);
12329
+ },
12330
+ };
12331
+ }
12332
+ }
12333
+ /**
12334
+ * Note: [💞] Ignore a discrepancy between file name and entity name
12335
+ */
12336
+
12337
+ /**
12338
+ * USE TIME commitment definition
12339
+ *
12340
+ * The `USE TIME` commitment indicates that the agent should be able to determine the current date and time.
12341
+ *
12342
+ * Example usage in agent source:
12343
+ *
12344
+ * ```book
12345
+ * USE TIME
12346
+ * ```
12347
+ *
12348
+ * @private [🪔] Maybe export the commitments through some package
12349
+ */
12350
+ class UseTimeCommitmentDefinition extends BaseCommitmentDefinition {
12351
+ constructor() {
12352
+ super('USE TIME', ['CURRENT TIME', 'TIME', 'DATE']);
12353
+ }
12354
+ /**
12355
+ * Short one-line description of USE TIME.
12356
+ */
12357
+ get description() {
12358
+ return 'Enable the agent to determine the current date and time.';
12359
+ }
12360
+ /**
12361
+ * Icon for this commitment.
12362
+ */
12363
+ get icon() {
12364
+ return '🕒';
12365
+ }
12366
+ /**
12367
+ * Markdown documentation for USE TIME commitment.
12368
+ */
12369
+ get documentation() {
12370
+ return spaceTrim$1(`
12371
+ # USE TIME
12372
+
12373
+ Enables the agent to determine the current date and time.
12374
+
12375
+ ## Key aspects
12376
+
12377
+ - This tool won't receive any input.
12378
+ - It outputs the current date and time as an ISO 8601 string.
12379
+ - Allows the agent to answer questions about the current time or date.
12380
+
12381
+ ## Examples
12382
+
12383
+ \`\`\`book
12384
+ Time-aware Assistant
12385
+
12386
+ PERSONA You are a helpful assistant who knows the current time.
12387
+ USE TIME
12388
+ \`\`\`
12389
+ `);
12390
+ }
12391
+ applyToAgentModelRequirements(requirements, content) {
12392
+ // Get existing tools array or create new one
12393
+ const existingTools = requirements.tools || [];
12394
+ // Add 'get_current_time' to tools if not already present
12395
+ const updatedTools = existingTools.some((tool) => tool.name === 'get_current_time')
12396
+ ? existingTools
12397
+ : [
12398
+ ...existingTools,
12399
+ {
12400
+ name: 'get_current_time',
12401
+ description: 'Get the current date and time in ISO 8601 format.',
12402
+ parameters: {
12403
+ type: 'object',
12404
+ properties: {
12405
+ timezone: {
12406
+ type: 'string',
12407
+ description: 'Optional timezone name (e.g. "Europe/Prague", "UTC", "America/New_York").',
12408
+ },
12409
+ },
12410
+ required: [],
12411
+ },
12412
+ },
12413
+ // <- TODO: !!!! define the function in LLM tools
12414
+ ];
12415
+ // Return requirements with updated tools and metadata
12416
+ return this.appendToSystemMessage({
12417
+ ...requirements,
12418
+ tools: updatedTools,
12419
+ metadata: {
12420
+ ...requirements.metadata,
12421
+ },
12422
+ }, spaceTrim$1(`
12423
+ You have access to the current date and time. Use it to answer questions about the current date and time.
12424
+ When you need to know the current date or time, use the tool provided to you.
12425
+ `));
12426
+ }
12427
+ /**
12428
+ * Gets human-readable titles for tool functions provided by this commitment.
12429
+ */
12430
+ getToolTitles() {
12431
+ return {
12432
+ get_current_time: 'Get current time',
12433
+ };
12434
+ }
12435
+ /**
12436
+ * Gets the `get_current_time` tool function implementation.
12437
+ */
12438
+ getToolFunctions() {
12439
+ return {
12440
+ async get_current_time(args) {
12441
+ var _a;
12442
+ console.log('!!!! [Tool] get_current_time called', { args });
12443
+ const { timezone } = args;
12444
+ if (!timezone) {
12445
+ return new Date().toISOString();
12446
+ }
12447
+ try {
12448
+ // Note: Returning ISO 8601 string but in the requested timezone
12449
+ const formatter = new Intl.DateTimeFormat('en-CA', {
12450
+ timeZone: timezone,
12451
+ year: 'numeric',
12452
+ month: '2-digit',
12453
+ day: '2-digit',
12454
+ hour: '2-digit',
12455
+ minute: '2-digit',
12456
+ second: '2-digit',
12457
+ hour12: false,
12458
+ timeZoneName: 'shortOffset',
12459
+ });
12460
+ const parts = formatter.formatToParts(new Date());
12461
+ const part = (type) => { var _a; return (_a = parts.find((p) => p.type === type)) === null || _a === void 0 ? void 0 : _a.value; };
12462
+ // en-CA format is YYYY-MM-DD
12463
+ const isoString = `${part('year')}-${part('month')}-${part('day')}T${part('hour')}:${part('minute')}:${part('second')}${(_a = part('timeZoneName')) === null || _a === void 0 ? void 0 : _a.replace('GMT', '')}`;
12464
+ return isoString;
12465
+ }
12466
+ catch (error) {
12467
+ // Fallback to UTC if timezone is invalid
12468
+ return new Date().toISOString();
12469
+ }
12470
+ },
11367
12471
  };
11368
12472
  }
11369
12473
  }
@@ -11438,7 +12542,6 @@ class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
11438
12542
  }
11439
12543
  }
11440
12544
 
11441
- // Import all commitment definition classes
11442
12545
  /**
11443
12546
  * Registry of all available commitment definitions
11444
12547
  * This array contains instances of all commitment definitions
@@ -11455,10 +12558,10 @@ const COMMITMENT_REGISTRY = [
11455
12558
  new MemoryCommitmentDefinition('MEMORIES'),
11456
12559
  new StyleCommitmentDefinition('STYLE'),
11457
12560
  new StyleCommitmentDefinition('STYLES'),
11458
- new RuleCommitmentDefinition('RULE'),
11459
12561
  new RuleCommitmentDefinition('RULES'),
11460
- new LanguageCommitmentDefinition('LANGUAGE'),
12562
+ new RuleCommitmentDefinition('RULE'),
11461
12563
  new LanguageCommitmentDefinition('LANGUAGES'),
12564
+ new LanguageCommitmentDefinition('LANGUAGE'),
11462
12565
  new SampleCommitmentDefinition('SAMPLE'),
11463
12566
  new SampleCommitmentDefinition('EXAMPLE'),
11464
12567
  new FormatCommitmentDefinition('FORMAT'),
@@ -11480,6 +12583,7 @@ const COMMITMENT_REGISTRY = [
11480
12583
  new NoteCommitmentDefinition('NOTES'),
11481
12584
  new NoteCommitmentDefinition('COMMENT'),
11482
12585
  new NoteCommitmentDefinition('NONCE'),
12586
+ new NoteCommitmentDefinition('TODO'),
11483
12587
  new GoalCommitmentDefinition('GOAL'),
11484
12588
  new GoalCommitmentDefinition('GOALS'),
11485
12589
  new InitialMessageCommitmentDefinition(),
@@ -11498,6 +12602,7 @@ const COMMITMENT_REGISTRY = [
11498
12602
  new ClosedCommitmentDefinition(),
11499
12603
  new UseBrowserCommitmentDefinition(),
11500
12604
  new UseSearchEngineCommitmentDefinition(),
12605
+ new UseTimeCommitmentDefinition(),
11501
12606
  new UseMcpCommitmentDefinition(),
11502
12607
  new UseCommitmentDefinition(),
11503
12608
  // Not yet implemented commitments (using placeholder)
@@ -11507,6 +12612,7 @@ const COMMITMENT_REGISTRY = [
11507
12612
  new NotYetImplementedCommitmentDefinition('AVOID'),
11508
12613
  new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
11509
12614
  new NotYetImplementedCommitmentDefinition('CONTEXT'),
12615
+ // <- TODO: Prompt: Leverage aliases instead of duplicating commitment definitions
11510
12616
  ];
11511
12617
  /**
11512
12618
  * Gets a commitment definition by its type
@@ -11586,42 +12692,38 @@ function getGroupedCommitmentDefinitions() {
11586
12692
  return $deepFreeze(groupedCommitments);
11587
12693
  }
11588
12694
  /**
11589
- * TODO: [🧠] Maybe create through standardized $register
11590
- * Note: [💞] Ignore a discrepancy between file name and entity name
11591
- */
11592
-
11593
- /**
11594
- * Creates an empty/basic agent model requirements object
11595
- * This serves as the starting point for the reduce-like pattern
11596
- * where each commitment applies its changes to build the final requirements
12695
+ * Gets all function implementations provided by all commitments
11597
12696
  *
11598
12697
  * @public exported from `@promptbook/core`
11599
12698
  */
11600
- function createEmptyAgentModelRequirements() {
11601
- return {
11602
- systemMessage: '',
11603
- // modelName: 'gpt-5',
11604
- modelName: 'gemini-2.5-flash-lite',
11605
- temperature: 0.7,
11606
- topP: 0.9,
11607
- topK: 50,
11608
- };
12699
+ function getAllCommitmentsToolFunctions() {
12700
+ const allToolFunctions = {};
12701
+ for (const commitmentDefinition of getAllCommitmentDefinitions()) {
12702
+ const toolFunctions = commitmentDefinition.getToolFunctions();
12703
+ for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
12704
+ allToolFunctions[funcName] = funcImpl;
12705
+ }
12706
+ }
12707
+ return allToolFunctions;
11609
12708
  }
11610
12709
  /**
11611
- * Creates a basic agent model requirements with just the agent name
11612
- * This is used when we have an agent name but no commitments
12710
+ * Gets all tool titles provided by all commitments
11613
12711
  *
11614
12712
  * @public exported from `@promptbook/core`
11615
12713
  */
11616
- function createBasicAgentModelRequirements(agentName) {
11617
- const empty = createEmptyAgentModelRequirements();
11618
- return {
11619
- ...empty,
11620
- systemMessage: `You are ${agentName || 'AI Agent'}`,
11621
- };
12714
+ function getAllCommitmentsToolTitles() {
12715
+ const allToolTitles = {};
12716
+ for (const commitmentDefinition of getAllCommitmentDefinitions()) {
12717
+ const toolTitles = commitmentDefinition.getToolTitles();
12718
+ for (const [funcName, title] of Object.entries(toolTitles)) {
12719
+ allToolTitles[funcName] = title;
12720
+ }
12721
+ }
12722
+ return allToolTitles;
11622
12723
  }
11623
12724
  /**
11624
- * TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
12725
+ * TODO: [🧠] Maybe create through standardized $register
12726
+ * Note: [💞] Ignore a discrepancy between file name and entity name
11625
12727
  */
11626
12728
 
11627
12729
  /**
@@ -11702,11 +12804,37 @@ function parseAgentSourceWithCommitments(agentSource) {
11702
12804
  let currentCommitment = null;
11703
12805
  // Process lines starting from after the agent name line
11704
12806
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
12807
+ let isInsideCodeBlock = false;
11705
12808
  for (let i = startIndex; i < lines.length; i++) {
11706
12809
  const line = lines[i];
11707
12810
  if (line === undefined) {
11708
12811
  continue;
11709
12812
  }
12813
+ const trimmedLine = line.trim();
12814
+ // Check if this line starts or ends a code block
12815
+ if (trimmedLine.startsWith('```')) {
12816
+ isInsideCodeBlock = !isInsideCodeBlock;
12817
+ if (currentCommitment) {
12818
+ // If we are inside a commitment, the code block is part of it
12819
+ currentCommitment.contentLines.push(line);
12820
+ }
12821
+ else {
12822
+ // If we are not inside a commitment, the code block is non-commitment
12823
+ nonCommitmentLines.push(line);
12824
+ }
12825
+ continue;
12826
+ }
12827
+ if (isInsideCodeBlock) {
12828
+ if (currentCommitment) {
12829
+ // If we are inside a commitment and a code block, the line is part of the commitment
12830
+ currentCommitment.contentLines.push(line);
12831
+ }
12832
+ else {
12833
+ // If we are inside a code block but not a commitment, the line is non-commitment
12834
+ nonCommitmentLines.push(line);
12835
+ }
12836
+ continue;
12837
+ }
11710
12838
  // Check if this line starts a new commitment
11711
12839
  let foundNewCommitment = false;
11712
12840
  for (const definition of COMMITMENT_REGISTRY) {
@@ -11784,6 +12912,97 @@ function parseAgentSourceWithCommitments(agentSource) {
11784
12912
  };
11785
12913
  }
11786
12914
 
12915
+ /**
12916
+ * Plugin for importing agent books *(`.book` files)*
12917
+ *
12918
+ * @private [🥝] Maybe export the import plugins through some package
12919
+ */
12920
+ const AgentFileImportPlugin = {
12921
+ name: 'agent-file-import-plugin',
12922
+ canImport(mimeType) {
12923
+ // [🧠] Should we have a specific MIME type for agent books?
12924
+ // For now, let's assume it's identified by .book extension or certain MIME types if provided
12925
+ return mimeType === 'text/x-promptbook' || mimeType === 'application/x-promptbook';
12926
+ },
12927
+ import(content) {
12928
+ const parseResult = parseAgentSourceWithCommitments(content);
12929
+ // Bring only the agent corpus (non-commitment lines and relevant commitments)
12930
+ // Stripping the agent name (which is usually the first line)
12931
+ const corpus = parseResult.nonCommitmentLines
12932
+ .filter((line, index) => index > 0 || !parseResult.agentName)
12933
+ .join('\n')
12934
+ .trim();
12935
+ // Also include relevant commitments that make up the "corpus" of the agent
12936
+ // For example PERSONA, RULE, KNOWLEDGE
12937
+ const relevantCommitments = parseResult.commitments
12938
+ .filter((c) => ['PERSONA', 'RULE', 'KNOWLEDGE'].includes(c.type))
12939
+ .map((c) => `${c.type} ${c.content}`)
12940
+ .join('\n\n');
12941
+ return spaceTrim$1((block) => `
12942
+ ${block(relevantCommitments)}
12943
+
12944
+ ${block(corpus)}
12945
+ `).trim();
12946
+ },
12947
+ };
12948
+
12949
+ /**
12950
+ * Plugin for importing JSON files
12951
+ *
12952
+ * @private [🥝] Maybe export the import plugins through some package
12953
+ */
12954
+ const JsonFileImportPlugin = {
12955
+ name: 'json-file-import-plugin',
12956
+ canImport(mimeType) {
12957
+ return mimeType === 'application/json' || mimeType.endsWith('+json');
12958
+ },
12959
+ import(content) {
12960
+ try {
12961
+ const json = JSON.parse(content);
12962
+ const formattedJson = JSON.stringify(json, null, 4);
12963
+ return `\`\`\`json\n${formattedJson}\n\`\`\``;
12964
+ }
12965
+ catch (error) {
12966
+ // If JSON is invalid, still import it but maybe not as pretty JSON
12967
+ return `\`\`\`json\n${content}\n\`\`\``;
12968
+ }
12969
+ },
12970
+ };
12971
+
12972
+ /**
12973
+ * Plugin for importing generic text files
12974
+ *
12975
+ * @private [🥝] Maybe export the import plugins through some package
12976
+ */
12977
+ const TextFileImportPlugin = {
12978
+ name: 'text-file-import-plugin',
12979
+ canImport(mimeType) {
12980
+ return (mimeType === 'text/plain' ||
12981
+ mimeType === 'text/markdown' ||
12982
+ mimeType === 'text/x-typescript' ||
12983
+ mimeType === 'text/javascript' ||
12984
+ mimeType === 'text/css' ||
12985
+ mimeType === 'text/html' ||
12986
+ mimeType.startsWith('text/'));
12987
+ },
12988
+ import(content, mimeType) {
12989
+ const extension = mimeTypeToExtension(mimeType);
12990
+ const codeBlockType = extension || 'txt';
12991
+ return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
12992
+ },
12993
+ };
12994
+
12995
+ /**
12996
+ * All available file import plugins
12997
+ *
12998
+ * @private [🥝] Maybe export the import plugins through some package
12999
+ */
13000
+ const $fileImportPlugins = [
13001
+ AgentFileImportPlugin,
13002
+ JsonFileImportPlugin,
13003
+ TextFileImportPlugin,
13004
+ ];
13005
+
11787
13006
  /**
11788
13007
  * Parses parameters from text using both supported notations:
11789
13008
  * 1. @Parameter - single word parameter starting with @
@@ -11873,6 +13092,7 @@ function removeCommentsFromSystemMessage(systemMessage) {
11873
13092
  * @public exported from `@promptbook/core`
11874
13093
  */
11875
13094
  async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
13095
+ var _a;
11876
13096
  // Parse the agent source to extract commitments
11877
13097
  const parseResult = parseAgentSourceWithCommitments(agentSource);
11878
13098
  // Apply DELETE filtering: remove prior commitments tagged by parameters targeted by DELETE/CANCEL/DISCARD/REMOVE
@@ -11939,6 +13159,64 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11939
13159
  }
11940
13160
  }
11941
13161
  }
13162
+ // Handle IMPORT commitments for generic files
13163
+ // Note: This logic could be moved to ImportCommitmentDefinition, but it needs to be asynchronous
13164
+ if (requirements.importedFileUrls && requirements.importedFileUrls.length > 0) {
13165
+ for (const fileUrl of requirements.importedFileUrls) {
13166
+ try {
13167
+ // 1. Mocked security check
13168
+ await mockedSecurityCheck(fileUrl);
13169
+ // 2. Fetch file content
13170
+ let content;
13171
+ let mimeType = null;
13172
+ if (isValidUrl(fileUrl)) {
13173
+ const response = await promptbookFetch(fileUrl);
13174
+ if (!response.ok) {
13175
+ throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`);
13176
+ }
13177
+ content = await response.text();
13178
+ mimeType = response.headers.get('Content-Type');
13179
+ /*
13180
+ TODO: !!!! Commented out this case because we need to work in Browser-compatible mode in many packages, use passed `fs` instead
13181
+ } else if (isValidFilePath(fileUrl)) {
13182
+ // [x🟢x] This code is expected to run in Node environment if local files are used
13183
+ const fs = await import('fs/promises');
13184
+ content = await fs.readFile(fileUrl, 'utf-8');
13185
+ const extension = getFileExtension(fileUrl);
13186
+ mimeType = extensionToMimeType(extension as string);
13187
+ */
13188
+ }
13189
+ else {
13190
+ throw new Error(`Invalid file URL or path: ${fileUrl}`);
13191
+ }
13192
+ if (!mimeType) {
13193
+ mimeType = 'text/plain';
13194
+ }
13195
+ // Remove charset from mime type
13196
+ mimeType = mimeType.split(';')[0].trim();
13197
+ // 3. Prevent importing binary files (mocked check)
13198
+ if (isBinaryMimeType(mimeType)) {
13199
+ throw new Error(`Importing binary files is not allowed: ${mimeType}`);
13200
+ }
13201
+ // 4. Find appropriate plugin
13202
+ const plugin = $fileImportPlugins.find((p) => p.canImport(mimeType));
13203
+ if (!plugin) {
13204
+ throw new Error(`No import plugin found for MIME type: ${mimeType}`);
13205
+ }
13206
+ // 5. Process content
13207
+ const importedContent = await plugin.import(content, mimeType);
13208
+ // 6. Append to system message
13209
+ requirements = {
13210
+ ...requirements,
13211
+ systemMessage: requirements.systemMessage + '\n\n' + importedContent,
13212
+ };
13213
+ }
13214
+ catch (error) {
13215
+ console.warn(`Failed to import file ${fileUrl}:`, error);
13216
+ // Continue with other imports even if one fails
13217
+ }
13218
+ }
13219
+ }
11942
13220
  // Handle MCP servers (extract from original agent source)
11943
13221
  const mcpServers = extractMcpServers(agentSource);
11944
13222
  if (mcpServers.length > 0) {
@@ -11948,9 +13226,11 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11948
13226
  };
11949
13227
  }
11950
13228
  // Add non-commitment lines to system message if they exist
13229
+ // Note: Filtering out horizontal lines (---) as requested
11951
13230
  const nonCommitmentContent = parseResult.nonCommitmentLines
11952
13231
  .filter((line, index) => index > 0 || !parseResult.agentName) // Skip first line if it's the agent name
11953
13232
  .filter((line) => line.trim()) // Remove empty lines
13233
+ .filter((line) => !/^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/.test(line)) // Remove horizontal lines
11954
13234
  .join('\n')
11955
13235
  .trim();
11956
13236
  if (nonCommitmentContent) {
@@ -11959,6 +13239,26 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11959
13239
  systemMessage: requirements.systemMessage + '\n\n' + nonCommitmentContent,
11960
13240
  };
11961
13241
  }
13242
+ // Add example interactions to the system message
13243
+ const examples = [];
13244
+ // 1. Initial message as an example agent response
13245
+ const initialMessage = (_a = parseResult.commitments.find((c) => c.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
13246
+ if (initialMessage) {
13247
+ examples.push(`Agent: ${initialMessage}`);
13248
+ }
13249
+ // 2. User and Agent message pairs
13250
+ if (requirements.samples && requirements.samples.length > 0) {
13251
+ for (const sample of requirements.samples) {
13252
+ examples.push(`User: ${sample.question}\nAgent: ${sample.answer}`);
13253
+ }
13254
+ }
13255
+ if (examples.length > 0) {
13256
+ const exampleInteractionsContent = `Example interaction:\n\n${examples.join('\n\n')}`;
13257
+ requirements = {
13258
+ ...requirements,
13259
+ systemMessage: requirements.systemMessage + '\n\n' + exampleInteractionsContent,
13260
+ };
13261
+ }
11962
13262
  // Remove comment lines (lines starting with #) from the final system message
11963
13263
  // while preserving the original content with comments in metadata
11964
13264
  const cleanedSystemMessage = removeCommentsFromSystemMessage(requirements.systemMessage);
@@ -11967,6 +13267,36 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11967
13267
  systemMessage: cleanedSystemMessage,
11968
13268
  };
11969
13269
  }
13270
+ /**
13271
+ * Mocked security check for imported files
13272
+ *
13273
+ * @param urlOrPath - The URL or local path of the file to check
13274
+ * @returns A promise that resolves if the file is safe
13275
+ */
13276
+ async function mockedSecurityCheck(urlOrPath) {
13277
+ // TODO: Implement proper security checks
13278
+ await new Promise((resolve) => setTimeout(resolve, 10)); // Mock async delay
13279
+ if (urlOrPath.includes('malicious')) {
13280
+ throw new Error(`Security check failed for: ${urlOrPath}`);
13281
+ }
13282
+ }
13283
+ /**
13284
+ * Checks if the given MIME type belongs to a binary file
13285
+ *
13286
+ * @param mimeType - The MIME type to check
13287
+ * @returns True if it's a binary MIME type
13288
+ */
13289
+ function isBinaryMimeType(mimeType) {
13290
+ const binaryPrefixes = [
13291
+ 'image/',
13292
+ 'video/',
13293
+ 'audio/',
13294
+ 'application/octet-stream',
13295
+ 'application/pdf',
13296
+ 'application/zip',
13297
+ ];
13298
+ return binaryPrefixes.some((prefix) => mimeType.startsWith(prefix));
13299
+ }
11970
13300
 
11971
13301
  /**
11972
13302
  * Normalizes agent name from arbitrary string to valid agent name
@@ -12027,7 +13357,24 @@ function parseAgentSource(agentSource) {
12027
13357
  const meta = {};
12028
13358
  const links = [];
12029
13359
  const capabilities = [];
13360
+ const samples = [];
13361
+ let pendingUserMessage = null;
12030
13362
  for (const commitment of parseResult.commitments) {
13363
+ if (commitment.type === 'INITIAL MESSAGE') {
13364
+ samples.push({ question: null, answer: commitment.content });
13365
+ continue;
13366
+ }
13367
+ if (commitment.type === 'USER MESSAGE') {
13368
+ pendingUserMessage = commitment.content;
13369
+ continue;
13370
+ }
13371
+ if (commitment.type === 'AGENT MESSAGE') {
13372
+ if (pendingUserMessage !== null) {
13373
+ samples.push({ question: pendingUserMessage, answer: commitment.content });
13374
+ pendingUserMessage = null;
13375
+ }
13376
+ continue;
13377
+ }
12031
13378
  if (commitment.type === 'USE BROWSER') {
12032
13379
  capabilities.push({
12033
13380
  type: 'browser',
@@ -12044,6 +13391,71 @@ function parseAgentSource(agentSource) {
12044
13391
  });
12045
13392
  continue;
12046
13393
  }
13394
+ if (commitment.type === 'USE SEARCH') {
13395
+ capabilities.push({
13396
+ type: 'search-engine',
13397
+ label: 'Search Internet',
13398
+ iconName: 'Search',
13399
+ });
13400
+ continue;
13401
+ }
13402
+ if (commitment.type === 'USE TIME') {
13403
+ capabilities.push({
13404
+ type: 'time',
13405
+ label: 'Time',
13406
+ iconName: 'Clock',
13407
+ });
13408
+ continue;
13409
+ }
13410
+ if (commitment.type === 'FROM') {
13411
+ const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
13412
+ if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
13413
+ continue;
13414
+ }
13415
+ let label = content;
13416
+ let iconName = 'SquareArrowOutUpRight'; // Inheritance remote
13417
+ if (content.startsWith('./') || content.startsWith('../') || content.startsWith('/')) {
13418
+ label = content.split('/').pop() || content;
13419
+ iconName = 'SquareArrowUpRight'; // Inheritance local
13420
+ }
13421
+ if (content === 'VOID') {
13422
+ label = 'VOID';
13423
+ iconName = 'ShieldAlert'; // [🧠] Or some other icon for VOID
13424
+ }
13425
+ capabilities.push({
13426
+ type: 'inheritance',
13427
+ label,
13428
+ iconName,
13429
+ agentUrl: content,
13430
+ });
13431
+ continue;
13432
+ }
13433
+ if (commitment.type === 'IMPORT') {
13434
+ const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
13435
+ let label = content;
13436
+ let iconName = 'ExternalLink'; // Import remote
13437
+ try {
13438
+ if (content.startsWith('http://') || content.startsWith('https://')) {
13439
+ const url = new URL(content);
13440
+ label = url.hostname.replace(/^www\./, '') + '.../' + url.pathname.split('/').pop();
13441
+ iconName = 'ExternalLink';
13442
+ }
13443
+ else if (content.startsWith('./') || content.startsWith('../') || content.startsWith('/')) {
13444
+ label = content.split('/').pop() || content;
13445
+ iconName = 'Link'; // Import local
13446
+ }
13447
+ }
13448
+ catch (e) {
13449
+ // Invalid URL or path, keep default label
13450
+ }
13451
+ capabilities.push({
13452
+ type: 'import',
13453
+ label,
13454
+ iconName,
13455
+ agentUrl: content,
13456
+ });
13457
+ continue;
13458
+ }
12047
13459
  if (commitment.type === 'KNOWLEDGE') {
12048
13460
  const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
12049
13461
  let label = content;
@@ -12128,6 +13540,7 @@ function parseAgentSource(agentSource) {
12128
13540
  links,
12129
13541
  parameters,
12130
13542
  capabilities,
13543
+ samples,
12131
13544
  };
12132
13545
  }
12133
13546
  /**
@@ -16758,6 +18171,9 @@ function filterModels(llmTools, predicate) {
16758
18171
  return originalModels.filter(predicate);
16759
18172
  }
16760
18173
  },
18174
+ checkConfiguration() {
18175
+ return /* not await */ llmTools.checkConfiguration();
18176
+ },
16761
18177
  };
16762
18178
  // Helper function to validate if a model is allowed
16763
18179
  async function isModelAllowed(modelName) {
@@ -17064,31 +18480,14 @@ class MemoryStorage {
17064
18480
  * Intercepts LLM tools and counts total usage of the tools
17065
18481
  *
17066
18482
  * Note: It can take extended `LlmExecutionTools` and cache the
18483
+ * Note: Returns full proxy of all LLM tool properties and methods
17067
18484
  *
17068
18485
  * @param llmTools LLM tools to be intercepted with usage counting, it can contain extra methods like `totalUsage`
17069
- * @returns LLM tools with same functionality with added total cost counting
18486
+ * @returns Full proxy of LLM tools with same functionality with added caching
17070
18487
  * @public exported from `@promptbook/core`
17071
18488
  */
17072
18489
  function cacheLlmTools(llmTools, options = {}) {
17073
18490
  const { storage = new MemoryStorage(), isCacheReloaded = false, isVerbose = DEFAULT_IS_VERBOSE } = options;
17074
- const proxyTools = {
17075
- ...llmTools,
17076
- // <- Note: [🥫]
17077
- get title() {
17078
- return `${llmTools.title} (cached)`;
17079
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17080
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17081
- },
17082
- get description() {
17083
- return `${llmTools.description} (cached)`;
17084
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17085
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17086
- },
17087
- listModels() {
17088
- // TODO: [🧠] Should be model listing also cached?
17089
- return /* not await */ llmTools.listModels();
17090
- },
17091
- };
17092
18491
  const callCommonModel = async (prompt) => {
17093
18492
  var _a;
17094
18493
  const { parameters, content, modelRequirements } = prompt;
@@ -17185,6 +18584,16 @@ function cacheLlmTools(llmTools, options = {}) {
17185
18584
  }
17186
18585
  }
17187
18586
  }
18587
+ if (shouldCache && promptResult.toolCalls !== undefined) {
18588
+ // Note: Do not cache results that contain tool calls because they are dynamic and should be always fresh
18589
+ // For example, it doesn't make sense to cache the message 'What time is it? 3:30 pm' because when the question is asked another time, it can be a different time.
18590
+ shouldCache = false;
18591
+ if (isVerbose) {
18592
+ console.info('Not caching result that contains tool calls for key:', key, {
18593
+ toolCalls: promptResult.toolCalls,
18594
+ });
18595
+ }
18596
+ }
17188
18597
  if (shouldCache) {
17189
18598
  await storage.setItem(key, {
17190
18599
  date: $getCurrentDate(),
@@ -17211,27 +18620,55 @@ function cacheLlmTools(llmTools, options = {}) {
17211
18620
  }
17212
18621
  return promptResult;
17213
18622
  };
17214
- if (llmTools.callChatModel !== undefined) {
17215
- proxyTools.callChatModel = async (prompt) => {
17216
- return /* not await */ callCommonModel(prompt);
17217
- };
17218
- }
17219
- if (llmTools.callCompletionModel !== undefined) {
17220
- proxyTools.callCompletionModel = async (prompt) => {
17221
- return /* not await */ callCommonModel(prompt);
17222
- };
17223
- }
17224
- if (llmTools.callEmbeddingModel !== undefined) {
17225
- proxyTools.callEmbeddingModel = async (prompt) => {
17226
- return /* not await */ callCommonModel(prompt);
17227
- };
17228
- }
17229
- if (llmTools.callImageGenerationModel !== undefined) {
17230
- proxyTools.callImageGenerationModel = async (prompt) => {
17231
- return /* not await */ callCommonModel(prompt);
17232
- };
17233
- }
17234
- // <- Note: [🤖]
18623
+ // Create a Proxy to intercept all property access and ensure full proxying of all properties
18624
+ const proxyTools = new Proxy(llmTools, {
18625
+ get(target, prop, receiver) {
18626
+ // Handle title property
18627
+ if (prop === 'title') {
18628
+ return `${target.title} (cached)`;
18629
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
18630
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
18631
+ }
18632
+ // Handle description property
18633
+ if (prop === 'description') {
18634
+ return `${target.description} (cached)`;
18635
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
18636
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
18637
+ }
18638
+ // Handle callChatModel method
18639
+ if (prop === 'callChatModel' && target.callChatModel !== undefined) {
18640
+ return async (prompt) => {
18641
+ return /* not await */ callCommonModel(prompt);
18642
+ };
18643
+ }
18644
+ // Handle callCompletionModel method
18645
+ if (prop === 'callCompletionModel' && target.callCompletionModel !== undefined) {
18646
+ return async (prompt) => {
18647
+ return /* not await */ callCommonModel(prompt);
18648
+ };
18649
+ }
18650
+ // Handle callEmbeddingModel method
18651
+ if (prop === 'callEmbeddingModel' && target.callEmbeddingModel !== undefined) {
18652
+ return async (prompt) => {
18653
+ return /* not await */ callCommonModel(prompt);
18654
+ };
18655
+ }
18656
+ // Handle callImageGenerationModel method
18657
+ if (prop === 'callImageGenerationModel' && target.callImageGenerationModel !== undefined) {
18658
+ return async (prompt) => {
18659
+ return /* not await */ callCommonModel(prompt);
18660
+ };
18661
+ }
18662
+ // <- Note: [🤖]
18663
+ // For all other properties and methods, delegate to the original target
18664
+ const value = Reflect.get(target, prop, receiver);
18665
+ // If it's a function, bind it to the target to preserve context
18666
+ if (typeof value === 'function') {
18667
+ return value.bind(target);
18668
+ }
18669
+ return value;
18670
+ },
18671
+ });
17235
18672
  return proxyTools;
17236
18673
  }
17237
18674
  /**
@@ -18338,20 +19775,29 @@ class OpenAiCompatibleExecutionTools {
18338
19775
  });
18339
19776
  return availableModels;
18340
19777
  }
19778
+ /**
19779
+ * Calls OpenAI compatible API to use a chat model.
19780
+ */
18341
19781
  /**
18342
19782
  * Calls OpenAI compatible API to use a chat model.
18343
19783
  */
18344
19784
  async callChatModel(prompt) {
19785
+ return this.callChatModelStream(prompt, () => { });
19786
+ }
19787
+ /**
19788
+ * Calls OpenAI compatible API to use a chat model with streaming.
19789
+ */
19790
+ async callChatModelStream(prompt, onProgress) {
18345
19791
  // Deep clone prompt and modelRequirements to avoid mutation across calls
18346
19792
  const clonedPrompt = JSON.parse(JSON.stringify(prompt));
18347
19793
  // Use local Set for retried parameters to ensure independence and thread safety
18348
19794
  const retriedUnsupportedParameters = new Set();
18349
- return this.callChatModelWithRetry(clonedPrompt, clonedPrompt.modelRequirements, [], retriedUnsupportedParameters);
19795
+ return this.callChatModelWithRetry(clonedPrompt, clonedPrompt.modelRequirements, [], retriedUnsupportedParameters, onProgress);
18350
19796
  }
18351
19797
  /**
18352
19798
  * Internal method that handles parameter retry for chat model calls
18353
19799
  */
18354
- async callChatModelWithRetry(prompt, currentModelRequirements, attemptStack = [], retriedUnsupportedParameters = new Set()) {
19800
+ async callChatModelWithRetry(prompt, currentModelRequirements, attemptStack = [], retriedUnsupportedParameters = new Set(), onProgress) {
18355
19801
  var _a;
18356
19802
  if (this.options.isVerbose) {
18357
19803
  console.info(`💬 ${this.title} callChatModel call`, { prompt, currentModelRequirements });
@@ -18457,6 +19903,23 @@ class OpenAiCompatibleExecutionTools {
18457
19903
  const usage = this.computeUsage(content || '', responseMessage.content || '', rawResponse);
18458
19904
  totalUsage = addUsage(totalUsage, usage);
18459
19905
  if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
19906
+ if (onProgress) {
19907
+ onProgress({
19908
+ content: responseMessage.content || '',
19909
+ modelName: rawResponse.model || modelName,
19910
+ timing: { start, complete: $getCurrentDate() },
19911
+ usage: totalUsage,
19912
+ toolCalls: responseMessage.tool_calls.map((toolCall) => ({
19913
+ name: toolCall.function.name,
19914
+ arguments: toolCall.function.arguments,
19915
+ result: '',
19916
+ rawToolCall: toolCall,
19917
+ })),
19918
+ rawPromptContent,
19919
+ rawRequest,
19920
+ rawResponse,
19921
+ });
19922
+ }
18460
19923
  await forEachAsync(responseMessage.tool_calls, {}, async (toolCall) => {
18461
19924
  const functionName = toolCall.function.name;
18462
19925
  const functionArgs = toolCall.function.arguments;
@@ -18478,7 +19941,7 @@ class OpenAiCompatibleExecutionTools {
18478
19941
  const args = ${functionArgs};
18479
19942
  return await ${functionName}(args);
18480
19943
  `,
18481
- parameters: {}, // <- TODO: [🧠] What parameters to pass?
19944
+ parameters: prompt.parameters,
18482
19945
  });
18483
19946
  }
18484
19947
  catch (error) {
@@ -18584,7 +20047,7 @@ class OpenAiCompatibleExecutionTools {
18584
20047
  });
18585
20048
  // Remove the unsupported parameter and retry
18586
20049
  const modifiedModelRequirements = removeUnsupportedModelRequirement(currentModelRequirements, unsupportedParameter);
18587
- return this.callChatModelWithRetry(prompt, modifiedModelRequirements, attemptStack, retriedUnsupportedParameters);
20050
+ return this.callChatModelWithRetry(prompt, modifiedModelRequirements, attemptStack, retriedUnsupportedParameters, onProgress);
18588
20051
  }
18589
20052
  }
18590
20053
  throw new PipelineExecutionError(`Tool calling loop did not return a result from ${this.title}`);
@@ -19217,7 +20680,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19217
20680
  * Calls OpenAI API to use a chat model with streaming.
19218
20681
  */
19219
20682
  async callChatModelStream(prompt, onProgress) {
19220
- var _a, _b, _c;
20683
+ var _a, _b, _c, _d;
19221
20684
  if (this.options.isVerbose) {
19222
20685
  console.info('💬 OpenAI callChatModel call', { prompt });
19223
20686
  }
@@ -19272,6 +20735,157 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19272
20735
  }
19273
20736
  // Always add the current user message
19274
20737
  threadMessages.push({ role: 'user', content: rawPromptContent });
20738
+ // Check if tools are being used - if so, use non-streaming mode
20739
+ const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
20740
+ const start = $getCurrentDate();
20741
+ let complete;
20742
+ // [🐱‍🚀] When tools are present, we need to use the non-streaming Runs API
20743
+ // because streaming doesn't support tool execution flow properly
20744
+ if (hasTools) {
20745
+ onProgress({
20746
+ content: '',
20747
+ modelName: 'assistant',
20748
+ timing: { start, complete: $getCurrentDate() },
20749
+ usage: UNCERTAIN_USAGE,
20750
+ rawPromptContent,
20751
+ rawRequest: null,
20752
+ rawResponse: null,
20753
+ });
20754
+ const rawRequest = {
20755
+ assistant_id: this.assistantId,
20756
+ thread: {
20757
+ messages: threadMessages,
20758
+ },
20759
+ tools: mapToolsToOpenAi(modelRequirements.tools),
20760
+ };
20761
+ if (this.options.isVerbose) {
20762
+ console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
20763
+ }
20764
+ // Create thread and run
20765
+ const threadAndRun = await client.beta.threads.createAndRun(rawRequest);
20766
+ let run = threadAndRun;
20767
+ // Poll until run completes or requires action
20768
+ while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
20769
+ if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
20770
+ // Execute tools
20771
+ const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
20772
+ const toolOutputs = [];
20773
+ for (const toolCall of toolCalls) {
20774
+ if (toolCall.type === 'function') {
20775
+ const functionName = toolCall.function.name;
20776
+ const functionArgs = JSON.parse(toolCall.function.arguments);
20777
+ onProgress({
20778
+ content: '',
20779
+ modelName: 'assistant',
20780
+ timing: { start, complete: $getCurrentDate() },
20781
+ usage: UNCERTAIN_USAGE,
20782
+ rawPromptContent,
20783
+ rawRequest: null,
20784
+ rawResponse: null,
20785
+ toolCalls: [
20786
+ {
20787
+ name: functionName,
20788
+ arguments: toolCall.function.arguments,
20789
+ result: '',
20790
+ rawToolCall: toolCall,
20791
+ },
20792
+ ],
20793
+ });
20794
+ if (this.options.isVerbose) {
20795
+ console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
20796
+ }
20797
+ // Get execution tools for script execution
20798
+ const executionTools = this.options
20799
+ .executionTools;
20800
+ if (!executionTools || !executionTools.script) {
20801
+ throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
20802
+ }
20803
+ // TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
20804
+ const scriptTools = Array.isArray(executionTools.script)
20805
+ ? executionTools.script
20806
+ : [executionTools.script];
20807
+ let functionResponse;
20808
+ try {
20809
+ const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
20810
+ functionResponse = await scriptTool.execute({
20811
+ scriptLanguage: 'javascript',
20812
+ script: `
20813
+ const args = ${JSON.stringify(functionArgs)};
20814
+ return await ${functionName}(args);
20815
+ `,
20816
+ parameters: prompt.parameters,
20817
+ });
20818
+ if (this.options.isVerbose) {
20819
+ console.info(`✅ Tool ${functionName} executed:`, functionResponse);
20820
+ }
20821
+ }
20822
+ catch (error) {
20823
+ assertsError(error);
20824
+ functionResponse = spaceTrim$2((block) => `
20825
+
20826
+ The invoked tool \`${functionName}\` failed with error:
20827
+
20828
+ \`\`\`json
20829
+ ${block(JSON.stringify(serializeError(error), null, 4))}
20830
+ \`\`\`
20831
+
20832
+ `);
20833
+ console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
20834
+ console.error(error);
20835
+ }
20836
+ toolOutputs.push({
20837
+ tool_call_id: toolCall.id,
20838
+ output: functionResponse,
20839
+ });
20840
+ }
20841
+ }
20842
+ // Submit tool outputs
20843
+ run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
20844
+ tool_outputs: toolOutputs,
20845
+ });
20846
+ }
20847
+ else {
20848
+ // Wait a bit before polling again
20849
+ await new Promise((resolve) => setTimeout(resolve, 500));
20850
+ run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
20851
+ }
20852
+ }
20853
+ if (run.status !== 'completed') {
20854
+ throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
20855
+ }
20856
+ // Get messages from the thread
20857
+ const messages = await client.beta.threads.messages.list(run.thread_id);
20858
+ const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
20859
+ if (assistantMessages.length === 0) {
20860
+ throw new PipelineExecutionError('No assistant messages found after run completion');
20861
+ }
20862
+ const lastMessage = assistantMessages[0];
20863
+ const textContent = lastMessage.content.find((c) => c.type === 'text');
20864
+ if (!textContent || textContent.type !== 'text') {
20865
+ throw new PipelineExecutionError('No text content in assistant response');
20866
+ }
20867
+ complete = $getCurrentDate();
20868
+ const resultContent = textContent.text.value;
20869
+ const usage = UNCERTAIN_USAGE;
20870
+ // Progress callback with final result
20871
+ const finalChunk = {
20872
+ content: resultContent,
20873
+ modelName: 'assistant',
20874
+ timing: { start, complete },
20875
+ usage,
20876
+ rawPromptContent,
20877
+ rawRequest,
20878
+ rawResponse: { run, messages: messages.data },
20879
+ };
20880
+ onProgress(finalChunk);
20881
+ return exportJson({
20882
+ name: 'promptResult',
20883
+ message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
20884
+ order: [],
20885
+ value: finalChunk,
20886
+ });
20887
+ }
20888
+ // Streaming mode (without tools)
19275
20889
  const rawRequest = {
19276
20890
  // TODO: [👨‍👨‍👧‍👧] ...modelSettings,
19277
20891
  // TODO: [👨‍👨‍👧‍👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
@@ -19282,10 +20896,8 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19282
20896
  tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
19283
20897
  // <- TODO: Add user identification here> user: this.options.user,
19284
20898
  };
19285
- const start = $getCurrentDate();
19286
- let complete;
19287
20899
  if (this.options.isVerbose) {
19288
- console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
20900
+ console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
19289
20901
  }
19290
20902
  const stream = await client.beta.threads.createAndRunStream(rawRequest);
19291
20903
  stream.on('connect', () => {
@@ -19321,6 +20933,15 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19321
20933
  console.info('messageDone', message);
19322
20934
  }
19323
20935
  });
20936
+ // TODO: [🐱‍🚀] Handle tool calls in assistants
20937
+ // Note: OpenAI Assistant streaming with tool calls requires special handling.
20938
+ // The stream will pause when a tool call is needed, and we need to:
20939
+ // 1. Wait for the run to reach 'requires_action' status
20940
+ // 2. Execute the tool calls
20941
+ // 3. Submit tool outputs via a separate API call (not on the stream)
20942
+ // 4. Continue the run
20943
+ // This requires switching to non-streaming mode or using the Runs API directly.
20944
+ // For now, tools with assistants should use the non-streaming chat completions API instead.
19324
20945
  const rawResponse = await stream.finalMessages();
19325
20946
  if (this.options.isVerbose) {
19326
20947
  console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
@@ -19331,10 +20952,10 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19331
20952
  if (rawResponse[0].content.length !== 1) {
19332
20953
  throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
19333
20954
  }
19334
- if (((_a = rawResponse[0].content[0]) === null || _a === void 0 ? void 0 : _a.type) !== 'text') {
19335
- throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type} finalMessages content type from OpenAI`);
20955
+ if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
20956
+ throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.type} finalMessages content type from OpenAI`);
19336
20957
  }
19337
- const resultContent = (_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.text.value;
20958
+ const resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
19338
20959
  // <- TODO: [🧠] There are also annotations, maybe use them
19339
20960
  // eslint-disable-next-line prefer-const
19340
20961
  complete = $getCurrentDate();
@@ -19402,7 +21023,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19402
21023
  throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
19403
21024
  }
19404
21025
  // await this.playground();
19405
- const { name, instructions, knowledgeSources } = options;
21026
+ const { name, instructions, knowledgeSources, tools } = options;
19406
21027
  const client = await this.getClient();
19407
21028
  let vectorStoreId;
19408
21029
  // If knowledge sources are provided, create a vector store with them
@@ -19473,7 +21094,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19473
21094
  description: 'Assistant created via Promptbook',
19474
21095
  model: 'gpt-4o',
19475
21096
  instructions,
19476
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
21097
+ tools: [
21098
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
21099
+ { type: 'file_search' },
21100
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
21101
+ ],
19477
21102
  };
19478
21103
  // Attach vector store if created
19479
21104
  if (vectorStoreId) {
@@ -19498,7 +21123,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19498
21123
  if (!this.isCreatingNewAssistantsAllowed) {
19499
21124
  throw new NotAllowed(`Updating assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
19500
21125
  }
19501
- const { assistantId, name, instructions, knowledgeSources } = options;
21126
+ const { assistantId, name, instructions, knowledgeSources, tools } = options;
19502
21127
  const client = await this.getClient();
19503
21128
  let vectorStoreId;
19504
21129
  // If knowledge sources are provided, create a vector store with them
@@ -19567,7 +21192,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19567
21192
  const assistantUpdate = {
19568
21193
  name,
19569
21194
  instructions,
19570
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
21195
+ tools: [
21196
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
21197
+ { type: 'file_search' },
21198
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
21199
+ ],
19571
21200
  };
19572
21201
  if (vectorStoreId) {
19573
21202
  assistantUpdate.tool_resources = {
@@ -19608,6 +21237,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19608
21237
  */
19609
21238
  const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
19610
21239
  /**
21240
+ * TODO: [🙎] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
19611
21241
  * TODO: [🧠][🧙‍♂️] Maybe there can be some wizard for those who want to use just OpenAI
19612
21242
  * TODO: Maybe make custom OpenAiError
19613
21243
  * TODO: [🧠][🈁] Maybe use `isDeterministic` from options
@@ -19666,8 +21296,10 @@ class AgentLlmExecutionTools {
19666
21296
  }
19667
21297
  /**
19668
21298
  * Get cached or create agent model requirements
21299
+ *
21300
+ * Note: [🐤] This is names `getModelRequirements` *(not `getAgentModelRequirements`)* because in future these two will be united
19669
21301
  */
19670
- async getAgentModelRequirements() {
21302
+ async getModelRequirements() {
19671
21303
  if (this._cachedModelRequirements === null) {
19672
21304
  // Get available models from underlying LLM tools for best model selection
19673
21305
  const availableModels = await this.options.llmTools.listModels();
@@ -19737,9 +21369,25 @@ class AgentLlmExecutionTools {
19737
21369
  if (prompt.modelRequirements.modelVariant !== 'CHAT') {
19738
21370
  throw new Error('AgentLlmExecutionTools only supports chat prompts');
19739
21371
  }
19740
- const modelRequirements = await this.getAgentModelRequirements();
21372
+ const modelRequirements = await this.getModelRequirements();
19741
21373
  const chatPrompt = prompt;
19742
21374
  let underlyingLlmResult;
21375
+ // Create modified chat prompt with agent system message
21376
+ const promptWithAgentModelRequirements = {
21377
+ ...chatPrompt,
21378
+ modelRequirements: {
21379
+ ...chatPrompt.modelRequirements,
21380
+ ...modelRequirements,
21381
+ // Spread tools to convert readonly array to mutable
21382
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
21383
+ // Prepend agent system message to existing system message
21384
+ systemMessage: modelRequirements.systemMessage +
21385
+ (chatPrompt.modelRequirements.systemMessage
21386
+ ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
21387
+ : ''),
21388
+ },
21389
+ };
21390
+ console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
19743
21391
  if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
19744
21392
  const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
19745
21393
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
@@ -19760,6 +21408,7 @@ class AgentLlmExecutionTools {
19760
21408
  name: this.title,
19761
21409
  instructions: modelRequirements.systemMessage,
19762
21410
  knowledgeSources: modelRequirements.knowledgeSources,
21411
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
19763
21412
  });
19764
21413
  AgentLlmExecutionTools.assistantCache.set(this.title, {
19765
21414
  assistantId: assistant.assistantId,
@@ -19776,6 +21425,7 @@ class AgentLlmExecutionTools {
19776
21425
  name: this.title,
19777
21426
  instructions: modelRequirements.systemMessage,
19778
21427
  knowledgeSources: modelRequirements.knowledgeSources,
21428
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
19779
21429
  /*
19780
21430
  !!!
19781
21431
  metadata: {
@@ -19788,32 +21438,28 @@ class AgentLlmExecutionTools {
19788
21438
  requirementsHash,
19789
21439
  });
19790
21440
  }
19791
- underlyingLlmResult = await assistant.callChatModelStream(chatPrompt, onProgress);
21441
+ // Create modified chat prompt with agent system message specific to OpenAI Assistant
21442
+ const promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools = {
21443
+ ...promptWithAgentModelRequirements,
21444
+ modelRequirements: {
21445
+ ...promptWithAgentModelRequirements.modelRequirements,
21446
+ modelName: undefined,
21447
+ systemMessage: undefined,
21448
+ temperature: undefined, // <- Note: Let the Assistant use its default temperature
21449
+ },
21450
+ };
21451
+ console.log('!!!! promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools:', promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools);
21452
+ underlyingLlmResult = await assistant.callChatModelStream(promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools, onProgress);
19792
21453
  }
19793
21454
  else {
19794
21455
  if (this.options.isVerbose) {
19795
21456
  console.log(`2️⃣ Creating Assistant ${this.title} on generic LLM execution tools...`);
19796
21457
  }
19797
- // Create modified chat prompt with agent system message
19798
- const modifiedChatPrompt = {
19799
- ...chatPrompt,
19800
- modelRequirements: {
19801
- ...chatPrompt.modelRequirements,
19802
- ...modelRequirements,
19803
- // Spread tools to convert readonly array to mutable
19804
- tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
19805
- // Prepend agent system message to existing system message
19806
- systemMessage: modelRequirements.systemMessage +
19807
- (chatPrompt.modelRequirements.systemMessage
19808
- ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
19809
- : ''),
19810
- },
19811
- };
19812
21458
  if (this.options.llmTools.callChatModelStream) {
19813
- underlyingLlmResult = await this.options.llmTools.callChatModelStream(modifiedChatPrompt, onProgress);
21459
+ underlyingLlmResult = await this.options.llmTools.callChatModelStream(promptWithAgentModelRequirements, onProgress);
19814
21460
  }
19815
21461
  else if (this.options.llmTools.callChatModel) {
19816
- underlyingLlmResult = await this.options.llmTools.callChatModel(modifiedChatPrompt);
21462
+ underlyingLlmResult = await this.options.llmTools.callChatModel(promptWithAgentModelRequirements);
19817
21463
  onProgress(underlyingLlmResult);
19818
21464
  }
19819
21465
  else {
@@ -19842,7 +21488,7 @@ AgentLlmExecutionTools.assistantCache = new Map();
19842
21488
  * TODO: [🧠] Adding parameter substitution support (here or should be responsibility of the underlying LLM Tools)
19843
21489
  */
19844
21490
 
19845
- var _Agent_instances, _Agent_selfLearn;
21491
+ var _Agent_instances, _Agent_selfLearnNonce, _Agent_selfLearnSamples, _Agent_selfLearnTeacher;
19846
21492
  /**
19847
21493
  * Represents one AI Agent
19848
21494
  *
@@ -19902,22 +21548,33 @@ class Agent extends AgentLlmExecutionTools {
19902
21548
  * This is parsed from commitments like USE BROWSER, USE SEARCH ENGINE, KNOWLEDGE, etc.
19903
21549
  */
19904
21550
  this.capabilities = [];
21551
+ /**
21552
+ * List of sample conversations (question/answer pairs)
21553
+ */
21554
+ this.samples = [];
19905
21555
  /**
19906
21556
  * Metadata like image or color
19907
21557
  */
19908
21558
  this.meta = {};
21559
+ /**
21560
+ * Human-readable titles for tool functions
21561
+ */
21562
+ this.toolTitles = {};
19909
21563
  // TODO: [🐱‍🚀] Add `Agent` simple "mocked" learning by appending to agent source
19910
21564
  // TODO: [🐱‍🚀] Add `Agent` learning by promptbookAgent
21565
+ this.teacherAgent = options.teacherAgent;
19911
21566
  this.agentSource = agentSource;
19912
21567
  this.agentSource.subscribe((source) => {
19913
21568
  this.updateAgentSource(source);
19914
- const { agentName, personaDescription, initialMessage, links, meta, capabilities } = parseAgentSource(source);
21569
+ const { agentName, personaDescription, initialMessage, links, meta, capabilities, samples } = parseAgentSource(source);
19915
21570
  this._agentName = agentName;
19916
21571
  this.personaDescription = personaDescription;
19917
21572
  this.initialMessage = initialMessage;
19918
21573
  this.links = links;
19919
21574
  this.capabilities = capabilities;
21575
+ this.samples = samples;
19920
21576
  this.meta = { ...this.meta, ...meta };
21577
+ this.toolTitles = getAllCommitmentsToolTitles();
19921
21578
  });
19922
21579
  }
19923
21580
  /**
@@ -19928,10 +21585,10 @@ class Agent extends AgentLlmExecutionTools {
19928
21585
  async callChatModelStream(prompt, onProgress) {
19929
21586
  var _a;
19930
21587
  // [1] Check if the user is asking the same thing as in the samples
19931
- const modelRequirements = await this.getAgentModelRequirements();
21588
+ const modelRequirements = await this.getModelRequirements();
19932
21589
  if (modelRequirements.samples) {
19933
21590
  const normalizedPrompt = normalizeMessageText(prompt.content);
19934
- const sample = modelRequirements.samples.find((sample) => normalizeMessageText(sample.question) === normalizedPrompt);
21591
+ const sample = modelRequirements.samples.find((sample) => sample.question !== null && normalizeMessageText(sample.question) === normalizedPrompt);
19935
21592
  if (sample) {
19936
21593
  const now = new Date().toISOString();
19937
21594
  const result = {
@@ -19977,21 +21634,42 @@ class Agent extends AgentLlmExecutionTools {
19977
21634
  if ((_a = modelRequirements.metadata) === null || _a === void 0 ? void 0 : _a.isClosed) {
19978
21635
  return result;
19979
21636
  }
19980
- await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearn).call(this, prompt, result);
19981
- // <- TODO: !!!! Do not await self-learn, run in background with error handling
21637
+ // TODO: !!!!! Return the answer and do the learning asynchronously
21638
+ // Note: [0] Asynchronously add nonce
21639
+ if (just(false)) {
21640
+ await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearnNonce).call(this);
21641
+ }
21642
+ // Note: [1] Do the append of the samples
21643
+ await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearnSamples).call(this, prompt, result);
21644
+ // Note: [2] Asynchronously call the teacher agent and invoke the silver link. When the teacher fails, keep just the samples
21645
+ await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearnTeacher).call(this, prompt, result).catch((error) => {
21646
+ // !!!!! if (this.options.isVerbose) {
21647
+ console.error(colors.bgCyan('[Self-learning]') + colors.red(' Failed to learn from teacher agent'));
21648
+ console.error(error);
21649
+ // }
21650
+ });
19982
21651
  return result;
19983
21652
  }
19984
21653
  }
19985
- _Agent_instances = new WeakSet(), _Agent_selfLearn =
21654
+ _Agent_instances = new WeakSet(), _Agent_selfLearnNonce =
19986
21655
  /**
19987
- * Self-learning: Appends the conversation and extracted knowledge to the agent source
21656
+ * Self-learning Step 0: Asynchronously with random timing add nonce to the agent source
19988
21657
  */
19989
- async function _Agent_selfLearn(prompt, result) {
19990
- // Learning: Append the conversation sample to the agent source
21658
+ async function _Agent_selfLearnNonce() {
21659
+ await forTime(Math.random() * 5000);
21660
+ // <- TODO: [🕓] `await forRandom(...)`
21661
+ console.info(colors.bgCyan('[Self-learning]') + colors.cyan(' Nonce'));
21662
+ const nonce = `NONCE ${await linguisticHash(Math.random().toString())}`;
21663
+ // Append to the current source
21664
+ const currentSource = this.agentSource.value;
21665
+ const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n---\n\n' + nonce));
21666
+ // <- TODO: [🈲] Use some object-based way how to append on book (with sections `---`)
21667
+ // Update the source (which will trigger the subscription and update the underlying tools)
21668
+ this.agentSource.next(newSource);
21669
+ }, _Agent_selfLearnSamples = function _Agent_selfLearnSamples(prompt, result) {
21670
+ console.info(colors.bgCyan('[Self-learning]') + colors.cyan(' Sampling'));
19991
21671
  const learningExample = spaceTrim$2((block) => `
19992
21672
 
19993
- ---
19994
-
19995
21673
  USER MESSAGE
19996
21674
  ${block(prompt.content)}
19997
21675
 
@@ -19999,53 +21677,81 @@ async function _Agent_selfLearn(prompt, result) {
19999
21677
  ${block(result.content)}
20000
21678
 
20001
21679
  `);
20002
- // Extract knowledge
20003
- let knowledgeBlock = '';
20004
- try {
20005
- const extractionPrompt = {
20006
- title: 'Knowledge Extraction',
20007
- modelRequirements: {
20008
- modelVariant: 'CHAT',
20009
- },
20010
- content: spaceTrim$2((block) => `
20011
- You are an AI agent that is learning from a conversation.
21680
+ // Append to the current source
21681
+ const currentSource = this.agentSource.value;
21682
+ const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n---\n\n' + learningExample));
21683
+ // <- TODO: [🈲] Use some object-based way how to append on book (with sections `---`)
21684
+ // Update the source (which will trigger the subscription and update the underlying tools)
21685
+ this.agentSource.next(newSource);
21686
+ }, _Agent_selfLearnTeacher =
21687
+ /**
21688
+ * Self-learning Step 2: Asynchronously call the teacher agent and invoke the silver link
21689
+ */
21690
+ async function _Agent_selfLearnTeacher(prompt, result) {
21691
+ // [1] Call the teacher agent // <- !!!!! Emojis
21692
+ if (this.teacherAgent === null) {
21693
+ return;
21694
+ }
21695
+ console.info(colors.bgCyan('[Self-learning]') + colors.cyan(' Teacher'));
21696
+ const teacherResult = await this.teacherAgent.callChatModel({
21697
+ title: 'Self-learning',
21698
+ modelRequirements: {
21699
+ modelVariant: 'CHAT',
21700
+ },
21701
+ // TODO: !!!! Use prompt notation
21702
+ content: spaceTrim$2((block) => `
20012
21703
 
20013
- Here is the conversation so far:
21704
+ You are a teacher agent helping another agent to learn from its interactions.
20014
21705
 
20015
- User: ${block(prompt.content)}
20016
- Agent: ${block(result.content)}
21706
+ Here is your current client which you are teaching:
20017
21707
 
20018
- Extract any new knowledge, facts, or important information that should be remembered for future interactions.
20019
- Format the output as a list of KNOWLEDGE blocks.
20020
- If there is no new knowledge, return nothing.
21708
+ \`\`\`book
21709
+ ${block(this.agentSource.value)}
21710
+ \`\`\`
20021
21711
 
20022
- Example output:
20023
- KNOWLEDGE The user's name is Alice.
20024
- KNOWLEDGE The project deadline is next Friday.
20025
- `),
20026
- pipelineUrl: 'https://github.com/webgptorg/promptbook/blob/main/prompts/knowledge-extraction.ptbk.md',
20027
- parameters: {},
20028
- };
20029
- if (this.options.llmTools.callChatModel) {
20030
- const extractionResult = await this.options.llmTools.callChatModel(extractionPrompt);
20031
- const extractedContent = extractionResult.content;
20032
- if (extractedContent.includes('KNOWLEDGE')) {
20033
- knowledgeBlock = '\n\n' + spaceTrim$2(extractedContent);
20034
- }
20035
- }
20036
- else {
20037
- // TODO: [🧠] Fallback to callChatModelStream if callChatModel is not available
20038
- }
20039
- }
20040
- catch (error) {
20041
- if (this.options.isVerbose) {
20042
- console.warn('Failed to extract knowledge', error);
20043
- }
21712
+ **And here is the latest interaction:**
21713
+
21714
+ **User:**
21715
+ ${block(prompt.content)}
21716
+
21717
+ **Agent:**
21718
+ ${block(result.content)}
21719
+
21720
+
21721
+ **Rules:**
21722
+
21723
+ - Decide what the agent should learn from this interaction.
21724
+ - Append new commitments at the end of the agent source.
21725
+ - Do not modify the current agent source, just return new commitments (KNOWLEDGE, RULE, etc.).
21726
+ - If there is nothing new to learn, return empty book code block
21727
+ - Wrap the commitments in a book code block.
21728
+ - Do not explain anything, just return the commitments wrapped in a book code block.
21729
+ - Write the learned commitments in the same style and language as in the original agent source.
21730
+
21731
+
21732
+ This is how book code block looks like:
21733
+
21734
+ \`\`\`book
21735
+ KNOWLEDGE The sky is blue.
21736
+ RULE Always be polite.
21737
+ \`\`\`
21738
+ `),
21739
+ // pipelineUrl: 'https://github.com/webgptorg/promptbook/blob/main/prompts/self-learning.ptbk.md',
21740
+ // <- TODO: !!!! Remove and `pipelineUrl` for agent purposes
21741
+ parameters: {},
21742
+ });
21743
+ console.log('!!!! teacherResult', teacherResult);
21744
+ const teacherCommitments = unwrapResult(teacherResult.content);
21745
+ if (teacherCommitments === '') {
21746
+ console.info(colors.bgCyan('[Self-learning]') +
21747
+ colors.cyan(' Teacher agent did not provide new commitments to learn'));
21748
+ return;
20044
21749
  }
20045
- // Append to the current source
21750
+ // [2] Append to the current source
20046
21751
  const currentSource = this.agentSource.value;
20047
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + learningExample + knowledgeBlock));
20048
- // Update the source (which will trigger the subscription and update the underlying tools)
21752
+ const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + teacherCommitments));
21753
+ // <- TODO: [🈲] Use some object-based way how to append on book (with sections `---`)
21754
+ // [3] Update the source
20049
21755
  this.agentSource.next(newSource);
20050
21756
  };
20051
21757
  /**
@@ -20190,10 +21896,25 @@ function book(strings, ...values) {
20190
21896
  */
20191
21897
  class RemoteAgent extends Agent {
20192
21898
  static async connect(options) {
20193
- console.log('[🐱‍🚀]', `${options.agentUrl}/api/profile`);
20194
- const profileResponse = await fetch(`${options.agentUrl}/api/profile`);
21899
+ const agentProfileUrl = `${options.agentUrl}/api/profile`;
21900
+ const profileResponse = await fetch(agentProfileUrl);
20195
21901
  // <- TODO: [🐱‍🚀] What about closed-source agents?
20196
21902
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
21903
+ if (!profileResponse.ok) {
21904
+ throw new Error(spaceTrim$2((block) => `
21905
+ Failed to fetch remote agent profile:
21906
+
21907
+ Agent URL:
21908
+ ${options.agentUrl}
21909
+
21910
+ Agent Profile URL:
21911
+ ${agentProfileUrl}
21912
+
21913
+ Http Error:
21914
+ ${block(profileResponse.statusText)}
21915
+
21916
+ `));
21917
+ }
20197
21918
  const profile = await profileResponse.json();
20198
21919
  // Note: We are creating dummy agent source because we don't have the source from the remote agent
20199
21920
  // But we populate the metadata from the profile
@@ -20220,6 +21941,7 @@ class RemoteAgent extends Agent {
20220
21941
  */
20221
21942
  },
20222
21943
  agentSource,
21944
+ teacherAgent: null, // <- Note:
20223
21945
  });
20224
21946
  remoteAgent._remoteAgentName = profile.agentName;
20225
21947
  remoteAgent._remoteAgentHash = profile.agentHash;
@@ -20227,11 +21949,13 @@ class RemoteAgent extends Agent {
20227
21949
  remoteAgent.initialMessage = profile.initialMessage;
20228
21950
  remoteAgent.links = profile.links;
20229
21951
  remoteAgent.meta = profile.meta;
21952
+ remoteAgent.toolTitles = profile.toolTitles || {};
20230
21953
  remoteAgent._isVoiceCallingEnabled = profile.isVoiceCallingEnabled === true; // [✨✷] Store voice calling status
20231
21954
  return remoteAgent;
20232
21955
  }
20233
21956
  constructor(options) {
20234
21957
  super(options);
21958
+ this.toolTitles = {};
20235
21959
  this._isVoiceCallingEnabled = false; // [✨✷] Track voice calling status
20236
21960
  this.agentUrl = options.agentUrl;
20237
21961
  }
@@ -20311,6 +22035,7 @@ class RemoteAgent extends Agent {
20311
22035
  // <- TODO: [🐱‍🚀] What about closed-source agents?
20312
22036
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
20313
22037
  let content = '';
22038
+ const toolCalls = [];
20314
22039
  if (!bookResponse.body) {
20315
22040
  content = await bookResponse.text();
20316
22041
  }
@@ -20326,6 +22051,36 @@ class RemoteAgent extends Agent {
20326
22051
  doneReading = !!done;
20327
22052
  if (value) {
20328
22053
  const textChunk = decoder.decode(value, { stream: true });
22054
+ let isHandled = false;
22055
+ try {
22056
+ const lines = textChunk.split('\n');
22057
+ for (const line of lines) {
22058
+ const trimmedLine = line.trim();
22059
+ if (trimmedLine.startsWith('{') && trimmedLine.endsWith('}')) {
22060
+ const chunk = JSON.parse(trimmedLine);
22061
+ if (chunk.toolCalls) {
22062
+ toolCalls.push(...chunk.toolCalls);
22063
+ onProgress({
22064
+ content,
22065
+ modelName: this.modelName,
22066
+ timing: {},
22067
+ usage: {},
22068
+ rawPromptContent: {},
22069
+ rawRequest: {},
22070
+ rawResponse: {},
22071
+ toolCalls: chunk.toolCalls,
22072
+ });
22073
+ isHandled = true;
22074
+ }
22075
+ }
22076
+ }
22077
+ }
22078
+ catch (error) {
22079
+ // Ignore non-json chunks
22080
+ }
22081
+ if (isHandled) {
22082
+ continue;
22083
+ }
20329
22084
  // console.debug('RemoteAgent chunk:', textChunk);
20330
22085
  content += textChunk;
20331
22086
  onProgress({
@@ -20336,6 +22091,7 @@ class RemoteAgent extends Agent {
20336
22091
  rawPromptContent: {},
20337
22092
  rawRequest: {},
20338
22093
  rawResponse: {},
22094
+ toolCalls,
20339
22095
  });
20340
22096
  }
20341
22097
  }
@@ -20351,6 +22107,7 @@ class RemoteAgent extends Agent {
20351
22107
  rawPromptContent: {},
20352
22108
  rawRequest: {},
20353
22109
  rawResponse: {},
22110
+ toolCalls,
20354
22111
  });
20355
22112
  }
20356
22113
  }
@@ -20367,6 +22124,7 @@ class RemoteAgent extends Agent {
20367
22124
  rawPromptContent: {},
20368
22125
  rawRequest: {},
20369
22126
  rawResponse: {},
22127
+ toolCalls,
20370
22128
  // <- TODO: [🐱‍🚀] Transfer and proxy the metadata
20371
22129
  };
20372
22130
  return agentResult;
@@ -21286,7 +23044,7 @@ const OpenAiSdkTranspiler = {
21286
23044
  {
21287
23045
  role: 'system',
21288
23046
  content: spaceTrim(\`
21289
- ${block(modelRequirements.systemMessage)}
23047
+ ${block(modelRequirements.systemMessage.split('`').join('\\`'))}
21290
23048
  \`),
21291
23049
  },
21292
23050
  ];
@@ -21372,7 +23130,7 @@ const OpenAiSdkTranspiler = {
21372
23130
  {
21373
23131
  role: 'system',
21374
23132
  content: spaceTrim(\`
21375
- ${block(modelRequirements.systemMessage)}
23133
+ ${block(modelRequirements.systemMessage.split('`').join('\\`'))}
21376
23134
  \`),
21377
23135
  },
21378
23136
  ];
@@ -21433,7 +23191,16 @@ const CORE_AGENTS_SERVER = {
21433
23191
  *
21434
23192
  * @public exported from `@promptbook/core`
21435
23193
  */
21436
- const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = { ADAM: 'adam', TEACHER: 'teacher' };
23194
+ const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = {
23195
+ /**
23196
+ * The default ancestor agent for new agents
23197
+ */
23198
+ ADAM: 'adam',
23199
+ /**
23200
+ * Agent that knows book syntax and can help with self-learning
23201
+ */
23202
+ TEACHER: 'teacher',
23203
+ };
21437
23204
  // <- TODO: [🆎] Allow to override (set) well-known agent names via Metadata
21438
23205
  /**
21439
23206
  * Available agents servers for the Promptbook
@@ -21542,29 +23309,82 @@ function $randomItem(...items) {
21542
23309
  * TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
21543
23310
  */
21544
23311
 
21545
- const PERSONALITIES = [
21546
- 'Friendly and helpful AI agent.',
21547
- 'Professional and efficient virtual assistant.',
21548
- 'Creative and imaginative digital companion.',
21549
- 'Knowledgeable and informative AI guide.',
21550
- 'Empathetic and understanding support bot.',
21551
- 'Energetic and enthusiastic conversational partner.',
21552
- 'Calm and patient virtual helper.',
21553
- 'Curious and inquisitive AI explorer.',
21554
- 'Witty and humorous digital friend.',
21555
- 'Serious and focused AI consultant.',
21556
- ];
23312
+ const PERSONALITIES = {
23313
+ ENGLISH: [
23314
+ 'Friendly and helpful AI agent.',
23315
+ 'Professional and efficient virtual assistant.',
23316
+ 'Creative and imaginative digital companion.',
23317
+ 'Knowledgeable and informative AI guide.',
23318
+ 'Empathetic and understanding support bot.',
23319
+ 'Energetic and enthusiastic conversational partner.',
23320
+ 'Calm and patient virtual helper.',
23321
+ 'Curious and inquisitive AI explorer.',
23322
+ 'Witty and humorous digital friend.',
23323
+ 'Serious and focused AI consultant.',
23324
+ ],
23325
+ CZECH: [
23326
+ // spell-checker:disable
23327
+ 'Přátelský a nápomocný AI agent.',
23328
+ 'Profesionální a efektivní virtuální asistent.',
23329
+ 'Kreativní a nápaditý digitální společník.',
23330
+ 'Zkušený a informativní AI průvodce.',
23331
+ 'Empatický a chápavý robot podpory.',
23332
+ 'Energický a nadšený partner pro konverzaci.',
23333
+ 'Klidný a trpělivý virtuální pomocník.',
23334
+ 'Zvědavý a hloubavý AI průzkumník.',
23335
+ 'Vtipný a humorný digitální přítel.',
23336
+ 'Vážný a soustředěný AI konzultant.',
23337
+ // spell-checker:enable
23338
+ ],
23339
+ };
21557
23340
  /**
21558
23341
  * Generates a random agent persona description.
21559
23342
  *
21560
23343
  * This function selects a random personality profile from a predefined pool
21561
23344
  * of common AI agent characteristics (e.g., friendly, professional, creative).
21562
23345
  *
23346
+ * @param language - The language code (e.g. 'ENGLISH', 'CZECH')
21563
23347
  * @returns A string describing the agent's persona
21564
23348
  * @private internal helper function
21565
23349
  */
21566
- function $randomAgentPersona() {
21567
- return $randomItem(...PERSONALITIES);
23350
+ function $randomAgentPersona(language = 'ENGLISH') {
23351
+ const normalizedLanguage = language.toUpperCase().trim();
23352
+ const personalities = PERSONALITIES[normalizedLanguage] || PERSONALITIES['ENGLISH'];
23353
+ return $randomItem(...personalities);
23354
+ }
23355
+ /**
23356
+ * TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
23357
+ */
23358
+
23359
+ const RULES = {
23360
+ ENGLISH: [
23361
+ 'Always prioritize user privacy and data security.',
23362
+ 'Respond in a friendly and approachable manner.',
23363
+ 'Avoid using technical jargon unless necessary.',
23364
+ 'Maintain a neutral and unbiased tone in all responses.',
23365
+ ],
23366
+ CZECH: [
23367
+ // spell-checker:disable
23368
+ 'Vždy upřednostňujte soukromí uživatelů a bezpečnost dat.',
23369
+ 'Odpovídejte přátelským a přístupným způsobem.',
23370
+ 'Vyhněte se používání technického žargonu, pokud to není nutné.',
23371
+ 'Udržujte ve všech odpovědích neutrální a nezaujatý tón.',
23372
+ // spell-checker:enable
23373
+ ],
23374
+ };
23375
+ /**
23376
+ * Generates a random agent rule description.
23377
+ *
23378
+ * This function selects a random rule
23379
+ *
23380
+ * @param language - The language code (e.g. 'ENGLISH', 'CZECH')
23381
+ * @returns A string describing the agent's rule
23382
+ * @private internal helper function
23383
+ */
23384
+ function $randomAgentRule(language = 'ENGLISH') {
23385
+ const normalizedLanguage = language.toUpperCase().trim();
23386
+ const rules = RULES[normalizedLanguage] || RULES['ENGLISH'];
23387
+ return $randomItem(...rules);
21568
23388
  }
21569
23389
  /**
21570
23390
  * TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
@@ -21850,10 +23670,9 @@ function getNamePool(language) {
21850
23670
  * @public exported from `@promptbook/core`
21851
23671
  */
21852
23672
  function $generateBookBoilerplate(options) {
21853
- // eslint-disable-next-line prefer-const
21854
- let { agentName, parentAgentName = 'Adam', personaDescription, meta, namePool = 'ENGLISH' } = options || {};
21855
- // eslint-disable-next-line prefer-const
21856
- let { image, color, ...restMeta } = meta || {};
23673
+ const { parentAgentName = 'Adam', initialRules = [], meta, namePool = 'ENGLISH' } = options || {};
23674
+ let { agentName, personaDescription } = options || {};
23675
+ let { color } = meta || {};
21857
23676
  if (!agentName) {
21858
23677
  const namePoolInstance = getNamePool(namePool);
21859
23678
  const randomFullnameWithColor = namePoolInstance.generateName();
@@ -21861,20 +23680,26 @@ function $generateBookBoilerplate(options) {
21861
23680
  color = color || randomFullnameWithColor.color;
21862
23681
  }
21863
23682
  if (!personaDescription) {
21864
- personaDescription = $randomAgentPersona();
23683
+ personaDescription = $randomAgentPersona(namePool);
23684
+ }
23685
+ if (initialRules.length === 0) {
23686
+ initialRules.push($randomAgentRule(namePool));
21865
23687
  }
21866
23688
  const agentSource = validateBook(spaceTrim$2((block) => `
21867
23689
  ${agentName}
21868
23690
 
21869
- META COLOR ${color || PROMPTBOOK_COLOR.toHex()}
21870
- META FONT Playfair Display, sans-serif
23691
+
21871
23692
  PERSONA ${block(personaDescription)}
23693
+ ${block(initialRules.map((rule) => `RULE ${rule}`).join('\n'))}
21872
23694
  `));
23695
+ // Note: `META COLOR ${color || PROMPTBOOK_COLOR.toHex()}` was removed for now
23696
+ // Note: `META FONT Playfair Display, sans-serif` was removed for now
23697
+ // <- TODO: [🈲] Simple and object-constructive way how to create new books
21873
23698
  return agentSource;
21874
23699
  }
21875
23700
  /**
21876
23701
  * TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
21877
23702
  */
21878
23703
 
21879
- export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, 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_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, 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, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createAgentModelRequirementsWithCommitments, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
23704
+ export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, 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_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, 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, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createAgentModelRequirementsWithCommitments, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolFunctions, getAllCommitmentsToolTitles, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
21880
23705
  //# sourceMappingURL=index.es.js.map