@promptbook/wizard 0.112.0-12 → 0.112.0-15

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/esm/index.es.js +714 -2522
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/cli/cli-commands/coder/{find-fresh-emoji-tag.d.ts → find-fresh-emoji-tags.d.ts} +1 -1
  4. package/esm/src/cli/cli-commands/coder.d.ts +1 -1
  5. package/esm/src/commitments/USE_BROWSER/resolveRunBrowserToolForNode.d.ts +1 -1
  6. package/esm/src/commitments/USE_TIMEOUT/TimeoutToolNames.d.ts +1 -0
  7. package/esm/src/commitments/USE_TIMEOUT/TimeoutToolRuntimeAdapter.d.ts +51 -2
  8. package/esm/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +2 -2
  9. package/esm/src/commitments/USE_TIMEOUT/getTimeoutToolRuntimeAdapterOrDisabledResult.d.ts +2 -2
  10. package/esm/src/commitments/USE_TIMEOUT/parseTimeoutToolArgs.d.ts +14 -1
  11. package/esm/src/execution/createPipelineExecutor/30-executeFormatSubvalues.d.ts +1 -1
  12. package/esm/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  13. package/esm/src/llm-providers/deepseek/deepseek-models.d.ts +1 -1
  14. package/esm/src/llm-providers/google/google-models.d.ts +1 -1
  15. package/esm/src/llm-providers/openai/openai-models.d.ts +1 -1
  16. package/esm/src/scrapers/_boilerplate/BoilerplateScraper.d.ts +1 -2
  17. package/esm/src/scrapers/document/DocumentScraper.d.ts +1 -2
  18. package/esm/src/scrapers/document-legacy/LegacyDocumentScraper.d.ts +1 -2
  19. package/esm/src/scripting/javascript/postprocessing-functions.d.ts +1 -1
  20. package/esm/src/utils/parameters/mapAvailableToExpectedParameters.d.ts +1 -2
  21. package/esm/src/version.d.ts +1 -1
  22. package/package.json +2 -3
  23. package/umd/index.umd.js +950 -2755
  24. package/umd/index.umd.js.map +1 -1
  25. package/umd/src/cli/cli-commands/coder/{find-fresh-emoji-tag.d.ts → find-fresh-emoji-tags.d.ts} +1 -1
  26. package/umd/src/cli/cli-commands/coder.d.ts +1 -1
  27. package/umd/src/commitments/USE_BROWSER/resolveRunBrowserToolForNode.d.ts +1 -1
  28. package/umd/src/commitments/USE_TIMEOUT/TimeoutToolNames.d.ts +1 -0
  29. package/umd/src/commitments/USE_TIMEOUT/TimeoutToolRuntimeAdapter.d.ts +51 -2
  30. package/umd/src/commitments/USE_TIMEOUT/USE_TIMEOUT.d.ts +2 -2
  31. package/umd/src/commitments/USE_TIMEOUT/getTimeoutToolRuntimeAdapterOrDisabledResult.d.ts +2 -2
  32. package/umd/src/commitments/USE_TIMEOUT/parseTimeoutToolArgs.d.ts +14 -1
  33. package/umd/src/execution/createPipelineExecutor/30-executeFormatSubvalues.d.ts +1 -1
  34. package/umd/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  35. package/umd/src/llm-providers/deepseek/deepseek-models.d.ts +1 -1
  36. package/umd/src/llm-providers/google/google-models.d.ts +1 -1
  37. package/umd/src/llm-providers/openai/openai-models.d.ts +1 -1
  38. package/umd/src/scrapers/_boilerplate/BoilerplateScraper.d.ts +1 -2
  39. package/umd/src/scrapers/document/DocumentScraper.d.ts +1 -2
  40. package/umd/src/scrapers/document-legacy/LegacyDocumentScraper.d.ts +1 -2
  41. package/umd/src/scripting/javascript/postprocessing-functions.d.ts +1 -1
  42. package/umd/src/utils/parameters/mapAvailableToExpectedParameters.d.ts +1 -2
  43. package/umd/src/version.d.ts +1 -1
  44. package/esm/apps/agents-server/config.d.ts +0 -86
  45. package/esm/apps/agents-server/src/tools/$provideBrowserForServer.d.ts +0 -28
  46. package/esm/apps/agents-server/src/tools/BrowserConnectionProvider.d.ts +0 -142
  47. package/esm/apps/agents-server/src/tools/runBrowserArtifacts.d.ts +0 -25
  48. package/esm/apps/agents-server/src/tools/runBrowserConstants.d.ts +0 -21
  49. package/esm/apps/agents-server/src/tools/runBrowserErrorHandling.d.ts +0 -60
  50. package/esm/apps/agents-server/src/tools/runBrowserErrors.d.ts +0 -73
  51. package/esm/apps/agents-server/src/tools/runBrowserObservability.d.ts +0 -36
  52. package/esm/apps/agents-server/src/tools/runBrowserResultFormatting.d.ts +0 -47
  53. package/esm/apps/agents-server/src/tools/runBrowserRuntime.d.ts +0 -24
  54. package/esm/apps/agents-server/src/tools/runBrowserWorkflow.d.ts +0 -36
  55. package/esm/apps/agents-server/src/tools/run_browser.d.ts +0 -11
  56. package/esm/apps/agents-server/src/utils/retryWithBackoff.d.ts +0 -95
  57. package/esm/apps/agents-server/src/utils/runBrowserArtifactStorage.d.ts +0 -26
  58. package/umd/apps/agents-server/config.d.ts +0 -86
  59. package/umd/apps/agents-server/src/tools/$provideBrowserForServer.d.ts +0 -28
  60. package/umd/apps/agents-server/src/tools/BrowserConnectionProvider.d.ts +0 -142
  61. package/umd/apps/agents-server/src/tools/runBrowserArtifacts.d.ts +0 -25
  62. package/umd/apps/agents-server/src/tools/runBrowserConstants.d.ts +0 -21
  63. package/umd/apps/agents-server/src/tools/runBrowserErrorHandling.d.ts +0 -60
  64. package/umd/apps/agents-server/src/tools/runBrowserErrors.d.ts +0 -73
  65. package/umd/apps/agents-server/src/tools/runBrowserObservability.d.ts +0 -36
  66. package/umd/apps/agents-server/src/tools/runBrowserResultFormatting.d.ts +0 -47
  67. package/umd/apps/agents-server/src/tools/runBrowserRuntime.d.ts +0 -24
  68. package/umd/apps/agents-server/src/tools/runBrowserWorkflow.d.ts +0 -36
  69. package/umd/apps/agents-server/src/tools/run_browser.d.ts +0 -11
  70. package/umd/apps/agents-server/src/utils/retryWithBackoff.d.ts +0 -95
  71. package/umd/apps/agents-server/src/utils/runBrowserArtifactStorage.d.ts +0 -26
package/esm/index.es.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { io } from 'socket.io-client';
2
- import spaceTrim$2, { spaceTrim as spaceTrim$1 } from 'spacetrim';
2
+ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
3
3
  import Anthropic from '@anthropic-ai/sdk';
4
4
  import Bottleneck from 'bottleneck';
5
5
  import colors from 'colors';
@@ -13,10 +13,7 @@ import { basename, join, dirname, isAbsolute, relative } from 'path';
13
13
  import { Readability } from '@mozilla/readability';
14
14
  import { JSDOM } from 'jsdom';
15
15
  import { Converter } from 'showdown';
16
- import { randomBytes, randomUUID } from 'crypto';
17
- import { tmpdir } from 'os';
18
- import { ConfigChecker } from 'configchecker';
19
- import { chromium } from 'playwright';
16
+ import { randomBytes } from 'crypto';
20
17
  import * as dotenv from 'dotenv';
21
18
  import sha256 from 'crypto-js/sha256';
22
19
  import JSZip from 'jszip';
@@ -41,7 +38,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
41
38
  * @generated
42
39
  * @see https://github.com/webgptorg/promptbook
43
40
  */
44
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-12';
41
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-15';
45
42
  /**
46
43
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
47
44
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1309,7 +1306,7 @@ function $deepFreeze(objectValue) {
1309
1306
  function getErrorReportUrl(error) {
1310
1307
  const report = {
1311
1308
  title: `🐜 Error report from ${NAME}`,
1312
- body: spaceTrim$2((block) => `
1309
+ body: spaceTrim$1((block) => `
1313
1310
 
1314
1311
 
1315
1312
  \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
@@ -1467,7 +1464,7 @@ function checkSerializableAsJson(options) {
1467
1464
  }
1468
1465
  else if (typeof value === 'object') {
1469
1466
  if (value instanceof Date) {
1470
- throw new UnexpectedError(spaceTrim$2((block) => `
1467
+ throw new UnexpectedError(spaceTrim$1((block) => `
1471
1468
  \`${name}\` is Date
1472
1469
 
1473
1470
  Use \`string_date_iso8601\` instead
@@ -1486,7 +1483,7 @@ function checkSerializableAsJson(options) {
1486
1483
  throw new UnexpectedError(`${name} is RegExp`);
1487
1484
  }
1488
1485
  else if (value instanceof Error) {
1489
- throw new UnexpectedError(spaceTrim$2((block) => `
1486
+ throw new UnexpectedError(spaceTrim$1((block) => `
1490
1487
  \`${name}\` is unserialized Error
1491
1488
 
1492
1489
  Use function \`serializeError\`
@@ -1509,7 +1506,7 @@ function checkSerializableAsJson(options) {
1509
1506
  }
1510
1507
  catch (error) {
1511
1508
  assertsError(error);
1512
- throw new UnexpectedError(spaceTrim$2((block) => `
1509
+ throw new UnexpectedError(spaceTrim$1((block) => `
1513
1510
  \`${name}\` is not serializable
1514
1511
 
1515
1512
  ${block(error.stack || error.message)}
@@ -1541,7 +1538,7 @@ function checkSerializableAsJson(options) {
1541
1538
  }
1542
1539
  }
1543
1540
  else {
1544
- throw new UnexpectedError(spaceTrim$2((block) => `
1541
+ throw new UnexpectedError(spaceTrim$1((block) => `
1545
1542
  \`${name}\` is unknown type
1546
1543
 
1547
1544
  Additional message for \`${name}\`:
@@ -2331,7 +2328,7 @@ function deserializeError(error, isStackAddedToMessage = true) {
2331
2328
  message = `${name}: ${message}`;
2332
2329
  }
2333
2330
  if (isStackAddedToMessage && stack !== undefined && stack !== '') {
2334
- message = spaceTrim$2((block) => `
2331
+ message = spaceTrim$1((block) => `
2335
2332
  ${block(message)}
2336
2333
 
2337
2334
  Original stack trace:
@@ -2389,7 +2386,7 @@ async function createRemoteClient(options) {
2389
2386
  const remoteServerUrlParsed = new URL(remoteServerUrl);
2390
2387
  if (remoteServerUrlParsed.pathname !== '/' && remoteServerUrlParsed.pathname !== '') {
2391
2388
  remoteServerUrlParsed.pathname = '/';
2392
- throw new Error(spaceTrim$2((block) => `
2389
+ throw new Error(spaceTrim$1((block) => `
2393
2390
  Remote server requires root url \`/\`
2394
2391
 
2395
2392
  You have provided \`remoteServerUrl\`:
@@ -2826,7 +2823,7 @@ function pricing(value) {
2826
2823
  /**
2827
2824
  * List of available Anthropic Claude models with pricing
2828
2825
  *
2829
- * Note: Synced with official API docs at 2025-11-19
2826
+ * Note: Synced with official API docs at 2026-03-22
2830
2827
  *
2831
2828
  * @see https://docs.anthropic.com/en/docs/models-overview
2832
2829
  * @public exported from `@promptbook/anthropic-claude`
@@ -2834,6 +2831,26 @@ function pricing(value) {
2834
2831
  const ANTHROPIC_CLAUDE_MODELS = exportJson({
2835
2832
  name: 'ANTHROPIC_CLAUDE_MODELS',
2836
2833
  value: [
2834
+ {
2835
+ modelVariant: 'CHAT',
2836
+ modelTitle: 'Claude Opus 4.6',
2837
+ modelName: 'claude-opus-4-6',
2838
+ modelDescription: "Anthropic's most capable model for advanced coding, complex reasoning, and agentic workflows with 1M token context window.",
2839
+ pricing: {
2840
+ prompt: pricing(`$5.00 / 1M tokens`),
2841
+ output: pricing(`$25.00 / 1M tokens`),
2842
+ },
2843
+ },
2844
+ {
2845
+ modelVariant: 'CHAT',
2846
+ modelTitle: 'Claude Sonnet 4.6',
2847
+ modelName: 'claude-sonnet-4-6',
2848
+ modelDescription: 'Best speed and intelligence balance for production-ready workloads with 1M token context window. Ideal for high-performance, lower-latency applications.',
2849
+ pricing: {
2850
+ prompt: pricing(`$3.00 / 1M tokens`),
2851
+ output: pricing(`$15.00 / 1M tokens`),
2852
+ },
2853
+ },
2837
2854
  {
2838
2855
  modelVariant: 'CHAT',
2839
2856
  modelTitle: 'Claude Sonnet 4.5',
@@ -3622,7 +3639,7 @@ class AnthropicClaudeExecutionTools {
3622
3639
  getDefaultModel(defaultModelName) {
3623
3640
  const model = ANTHROPIC_CLAUDE_MODELS.find(({ modelName }) => modelName.startsWith(defaultModelName));
3624
3641
  if (model === undefined) {
3625
- throw new UnexpectedError(spaceTrim$2((block) => `
3642
+ throw new UnexpectedError(spaceTrim$1((block) => `
3626
3643
  Cannot find model in Anthropic Claude models with name "${defaultModelName}" which should be used as default.
3627
3644
 
3628
3645
  Available models:
@@ -3779,7 +3796,7 @@ const _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
3779
3796
  /**
3780
3797
  * List of available OpenAI models with pricing
3781
3798
  *
3782
- * Note: Synced with official API docs at 2025-11-19
3799
+ * Note: Synced with official API docs at 2026-03-22
3783
3800
  *
3784
3801
  * @see https://platform.openai.com/docs/models/
3785
3802
  * @see https://openai.com/api/pricing/
@@ -3901,8 +3918,8 @@ const OPENAI_MODELS = exportJson({
3901
3918
  modelName: 'gpt-4.1',
3902
3919
  modelDescription: 'Smartest non-reasoning model with 128K context window. Enhanced version of GPT-4 with improved instruction following, better factual accuracy, and reduced hallucinations. Features advanced function calling capabilities and superior performance on coding tasks. Ideal for applications requiring high intelligence without reasoning overhead.',
3903
3920
  pricing: {
3904
- prompt: pricing(`$3.00 / 1M tokens`),
3905
- output: pricing(`$12.00 / 1M tokens`),
3921
+ prompt: pricing(`$2.00 / 1M tokens`),
3922
+ output: pricing(`$8.00 / 1M tokens`),
3906
3923
  },
3907
3924
  },
3908
3925
  /**/
@@ -3913,8 +3930,8 @@ const OPENAI_MODELS = exportJson({
3913
3930
  modelName: 'gpt-4.1-mini',
3914
3931
  modelDescription: 'Smaller, faster version of GPT-4.1 with 128K context window. Balances intelligence and efficiency with 3x faster inference than base GPT-4.1. Maintains strong capabilities across text generation, reasoning, and coding while offering better cost-performance ratio for most applications.',
3915
3932
  pricing: {
3916
- prompt: pricing(`$0.80 / 1M tokens`),
3917
- output: pricing(`$3.20 / 1M tokens`),
3933
+ prompt: pricing(`$0.40 / 1M tokens`),
3934
+ output: pricing(`$1.60 / 1M tokens`),
3918
3935
  },
3919
3936
  },
3920
3937
  /**/
@@ -3925,8 +3942,8 @@ const OPENAI_MODELS = exportJson({
3925
3942
  modelName: 'gpt-4.1-nano',
3926
3943
  modelDescription: 'Fastest, most cost-efficient version of GPT-4.1 with 128K context window. Optimized for high-throughput applications requiring good quality at minimal cost. Features 5x faster inference than GPT-4.1 while maintaining adequate performance for most general-purpose tasks.',
3927
3944
  pricing: {
3928
- prompt: pricing(`$0.20 / 1M tokens`),
3929
- output: pricing(`$0.80 / 1M tokens`),
3945
+ prompt: pricing(`$0.10 / 1M tokens`),
3946
+ output: pricing(`$0.40 / 1M tokens`),
3930
3947
  },
3931
3948
  },
3932
3949
  /**/
@@ -3937,8 +3954,8 @@ const OPENAI_MODELS = exportJson({
3937
3954
  modelName: 'o3',
3938
3955
  modelDescription: 'Advanced reasoning model with 128K context window specializing in complex logical, mathematical, and analytical tasks. Successor to o1 with enhanced step-by-step problem-solving capabilities and superior performance on STEM-focused problems. Ideal for professional applications requiring deep analytical thinking and precise reasoning.',
3939
3956
  pricing: {
3940
- prompt: pricing(`$15.00 / 1M tokens`),
3941
- output: pricing(`$60.00 / 1M tokens`),
3957
+ prompt: pricing(`$2.00 / 1M tokens`),
3958
+ output: pricing(`$8.00 / 1M tokens`),
3942
3959
  },
3943
3960
  },
3944
3961
  /**/
@@ -3949,8 +3966,8 @@ const OPENAI_MODELS = exportJson({
3949
3966
  modelName: 'o3-pro',
3950
3967
  modelDescription: 'Enhanced version of o3 with more compute allocated for better responses on the most challenging problems. Features extended reasoning time and improved accuracy on complex analytical tasks. Designed for applications where maximum reasoning quality is more important than response speed.',
3951
3968
  pricing: {
3952
- prompt: pricing(`$30.00 / 1M tokens`),
3953
- output: pricing(`$120.00 / 1M tokens`),
3969
+ prompt: pricing(`$20.00 / 1M tokens`),
3970
+ output: pricing(`$80.00 / 1M tokens`),
3954
3971
  },
3955
3972
  },
3956
3973
  /**/
@@ -3961,8 +3978,8 @@ const OPENAI_MODELS = exportJson({
3961
3978
  modelName: 'o4-mini',
3962
3979
  modelDescription: 'Fast, cost-efficient reasoning model with 128K context window. Successor to o1-mini with improved analytical capabilities while maintaining speed advantages. Features enhanced mathematical reasoning and logical problem-solving at significantly lower cost than full reasoning models.',
3963
3980
  pricing: {
3964
- prompt: pricing(`$4.00 / 1M tokens`),
3965
- output: pricing(`$16.00 / 1M tokens`),
3981
+ prompt: pricing(`$1.10 / 1M tokens`),
3982
+ output: pricing(`$4.40 / 1M tokens`),
3966
3983
  },
3967
3984
  },
3968
3985
  /**/
@@ -4320,8 +4337,8 @@ const OPENAI_MODELS = exportJson({
4320
4337
  modelName: 'gpt-4o-2024-05-13',
4321
4338
  modelDescription: 'May 2024 version of GPT-4o with 128K context window. Features enhanced multimodal capabilities including superior image understanding (up to 20MP), audio processing, and improved reasoning. Optimized for 2x lower latency than GPT-4 Turbo while maintaining high performance. Includes knowledge up to October 2023. Ideal for production applications requiring reliable multimodal capabilities.',
4322
4339
  pricing: {
4323
- prompt: pricing(`$5.00 / 1M tokens`),
4324
- output: pricing(`$15.00 / 1M tokens`),
4340
+ prompt: pricing(`$2.50 / 1M tokens`),
4341
+ output: pricing(`$10.00 / 1M tokens`),
4325
4342
  },
4326
4343
  },
4327
4344
  /**/
@@ -4332,8 +4349,8 @@ const OPENAI_MODELS = exportJson({
4332
4349
  modelName: 'gpt-4o',
4333
4350
  modelDescription: "OpenAI's most advanced general-purpose multimodal model with 128K context window. Optimized for balanced performance, speed, and cost with 2x faster responses than GPT-4 Turbo. Features excellent vision processing, audio understanding, reasoning, and text generation quality. Represents optimal balance of capability and efficiency for most advanced applications.",
4334
4351
  pricing: {
4335
- prompt: pricing(`$5.00 / 1M tokens`),
4336
- output: pricing(`$15.00 / 1M tokens`),
4352
+ prompt: pricing(`$2.50 / 1M tokens`),
4353
+ output: pricing(`$10.00 / 1M tokens`),
4337
4354
  },
4338
4355
  },
4339
4356
  /**/
@@ -4404,8 +4421,8 @@ const OPENAI_MODELS = exportJson({
4404
4421
  modelName: 'o3-mini',
4405
4422
  modelDescription: 'Cost-effective reasoning model with 128K context window optimized for academic and scientific problem-solving. Features efficient performance on STEM tasks with specialized capabilities in mathematics, physics, chemistry, and computer science. Offers 80% of O1 performance on technical domains at significantly lower cost. Ideal for educational applications and research support.',
4406
4423
  pricing: {
4407
- prompt: pricing(`$3.00 / 1M tokens`),
4408
- output: pricing(`$12.00 / 1M tokens`),
4424
+ prompt: pricing(`$1.10 / 1M tokens`),
4425
+ output: pricing(`$4.40 / 1M tokens`),
4409
4426
  },
4410
4427
  },
4411
4428
  /**/
@@ -4940,7 +4957,7 @@ function createExecutionToolsFromVercelProvider(options) {
4940
4957
  const modelName = modelRequirements.modelName ||
4941
4958
  ((_a = availableModels.find(({ modelVariant }) => modelVariant === 'CHAT')) === null || _a === void 0 ? void 0 : _a.modelName);
4942
4959
  if (!modelName) {
4943
- throw new PipelineExecutionError(spaceTrim$2(`
4960
+ throw new PipelineExecutionError(spaceTrim$1(`
4944
4961
  Can not determine which model to use.
4945
4962
 
4946
4963
  You need to provide at least one of:
@@ -5055,7 +5072,7 @@ function createExecutionToolsFromVercelProvider(options) {
5055
5072
  /**
5056
5073
  * List of available Deepseek models with descriptions
5057
5074
  *
5058
- * Note: Synced with official API docs at 2025-08-20
5075
+ * Note: Synced with official API docs at 2026-03-22
5059
5076
  *
5060
5077
  * @see https://www.deepseek.com/models
5061
5078
  * @public exported from `@promptbook/deepseek`
@@ -5069,8 +5086,8 @@ const DEEPSEEK_MODELS = exportJson({
5069
5086
  modelName: 'deepseek-chat',
5070
5087
  modelDescription: 'Latest flagship general-purpose model with 128K context window. Features exceptional reasoning capabilities, advanced code generation, and strong performance across diverse domains. Offers competitive performance with leading models while maintaining cost efficiency. Ideal for complex reasoning, coding, and knowledge-intensive tasks.',
5071
5088
  pricing: {
5072
- prompt: pricing(`$0.14 / 1M tokens`),
5073
- output: pricing(`$0.28 / 1M tokens`),
5089
+ prompt: pricing(`$0.28 / 1M tokens`),
5090
+ output: pricing(`$0.42 / 1M tokens`),
5074
5091
  },
5075
5092
  },
5076
5093
  {
@@ -5336,7 +5353,7 @@ const _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
5336
5353
  /**
5337
5354
  * List of available Google models with descriptions
5338
5355
  *
5339
- * Note: Synced with official API docs at 2025-11-19
5356
+ * Note: Synced with official API docs at 2026-03-22
5340
5357
  *
5341
5358
  * @see https://ai.google.dev/models/gemini
5342
5359
  * @public exported from `@promptbook/google`
@@ -5357,8 +5374,8 @@ const GOOGLE_MODELS = exportJson({
5357
5374
  modelName: 'gemini-2.5-pro',
5358
5375
  modelDescription: 'State-of-the-art thinking model with 1M token context window capable of reasoning over complex problems in code, math, and STEM. Features enhanced thinking capabilities, advanced multimodal understanding, and superior performance on analytical tasks. Ideal for complex enterprise applications requiring maximum intelligence and reasoning.',
5359
5376
  pricing: {
5360
- prompt: pricing(`$7.00 / 1M tokens`),
5361
- output: pricing(`$21.00 / 1M tokens`),
5377
+ prompt: pricing(`$1.25 / 1M tokens`),
5378
+ output: pricing(`$10.00 / 1M tokens`),
5362
5379
  },
5363
5380
  },
5364
5381
  {
@@ -5367,8 +5384,8 @@ const GOOGLE_MODELS = exportJson({
5367
5384
  modelName: 'gemini-2.5-flash',
5368
5385
  modelDescription: 'Best model in terms of price-performance with 1M token context window offering well-rounded capabilities. Features adaptive thinking, cost efficiency, and enhanced reasoning for large-scale processing. Ideal for low-latency, high-volume tasks that require thinking and agentic use cases.',
5369
5386
  pricing: {
5370
- prompt: pricing(`$0.35 / 1M tokens`),
5371
- output: pricing(`$1.05 / 1M tokens`),
5387
+ prompt: pricing(`$0.30 / 1M tokens`),
5388
+ output: pricing(`$2.50 / 1M tokens`),
5372
5389
  },
5373
5390
  },
5374
5391
  {
@@ -5377,8 +5394,8 @@ const GOOGLE_MODELS = exportJson({
5377
5394
  modelName: 'gemini-2.5-flash-lite',
5378
5395
  modelDescription: 'Cost-efficient Gemini 2.5 Flash model optimized for high throughput with 1M token context window. Features thinking capabilities while maintaining the most cost-efficient pricing. Perfect for real-time, low-latency use cases requiring good quality at scale.',
5379
5396
  pricing: {
5380
- prompt: pricing(`$0.20 / 1M tokens`),
5381
- output: pricing(`$0.60 / 1M tokens`),
5397
+ prompt: pricing(`$0.10 / 1M tokens`),
5398
+ output: pricing(`$0.40 / 1M tokens`),
5382
5399
  },
5383
5400
  },
5384
5401
  {
@@ -5898,188 +5915,6 @@ resultContent, rawResponse, duration = ZERO_VALUE) {
5898
5915
  * TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
5899
5916
  */
5900
5917
 
5901
- /**
5902
- * Prompt parameter key used to pass hidden runtime context to tool execution.
5903
- *
5904
- * @private internal runtime wiring for commitment tools
5905
- */
5906
- const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
5907
- /**
5908
- * Hidden argument key used to pass runtime context into individual tool calls.
5909
- *
5910
- * @private internal runtime wiring for commitment tools
5911
- */
5912
- const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
5913
- /**
5914
- * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
5915
- *
5916
- * @private internal runtime wiring for commitment tools
5917
- */
5918
- const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
5919
- /**
5920
- * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
5921
- *
5922
- * @private internal runtime wiring for commitment tools
5923
- */
5924
- const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
5925
- /**
5926
- * Monotonic counter used for hidden progress-listener tokens.
5927
- *
5928
- * @private internal runtime wiring for commitment tools
5929
- */
5930
- let toolCallProgressListenerCounter = 0;
5931
- /**
5932
- * Active tool-progress listeners keyed by hidden execution token.
5933
- *
5934
- * @private internal runtime wiring for commitment tools
5935
- */
5936
- const toolCallProgressListeners = new Map();
5937
- /**
5938
- * Parses unknown runtime context payload into a normalized object.
5939
- *
5940
- * @private internal runtime wiring for commitment tools
5941
- */
5942
- function parseToolRuntimeContext(rawValue) {
5943
- if (!rawValue) {
5944
- return null;
5945
- }
5946
- let parsed = rawValue;
5947
- if (typeof rawValue === 'string') {
5948
- try {
5949
- parsed = JSON.parse(rawValue);
5950
- }
5951
- catch (_a) {
5952
- return null;
5953
- }
5954
- }
5955
- if (!parsed || typeof parsed !== 'object') {
5956
- return null;
5957
- }
5958
- return parsed;
5959
- }
5960
- /**
5961
- * Reads runtime context attached to tool call arguments.
5962
- *
5963
- * @private internal runtime wiring for commitment tools
5964
- */
5965
- function readToolRuntimeContextFromToolArgs(args) {
5966
- return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
5967
- }
5968
- /**
5969
- * Reads the hidden tool-progress token from tool arguments.
5970
- *
5971
- * @private internal runtime wiring for commitment tools
5972
- */
5973
- function readToolProgressTokenFromToolArgs(args) {
5974
- const token = args[TOOL_PROGRESS_TOKEN_ARGUMENT];
5975
- return typeof token === 'string' && token.trim().length > 0 ? token : null;
5976
- }
5977
- /**
5978
- * Serializes runtime context for prompt parameters.
5979
- *
5980
- * @private internal runtime wiring for commitment tools
5981
- */
5982
- function serializeToolRuntimeContext(context) {
5983
- return JSON.stringify(context);
5984
- }
5985
- /**
5986
- * Registers one in-memory listener that receives progress updates emitted by a running tool.
5987
- *
5988
- * The returned token is passed into script execution as a hidden argument so tool implementations
5989
- * can stream progress without exposing extra parameters to the model.
5990
- *
5991
- * @param listener - Listener notified about tool progress.
5992
- * @returns Hidden token used to route progress updates.
5993
- * @private internal runtime wiring for commitment tools
5994
- */
5995
- function registerToolCallProgressListener(listener) {
5996
- toolCallProgressListenerCounter += 1;
5997
- const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
5998
- toolCallProgressListeners.set(token, listener);
5999
- return token;
6000
- }
6001
- /**
6002
- * Unregisters one in-memory progress listener.
6003
- *
6004
- * @param token - Token previously created by `registerToolCallProgressListener`.
6005
- * @private internal runtime wiring for commitment tools
6006
- */
6007
- function unregisterToolCallProgressListener(token) {
6008
- toolCallProgressListeners.delete(token);
6009
- }
6010
- /**
6011
- * Emits one tool progress update using a hidden token carried in tool arguments.
6012
- *
6013
- * @param args - Raw tool arguments including hidden runtime keys.
6014
- * @param update - Incremental progress update.
6015
- * @returns `true` when a listener was found and notified.
6016
- * @private internal runtime wiring for commitment tools
6017
- */
6018
- function emitToolCallProgressFromToolArgs(args, update) {
6019
- const token = readToolProgressTokenFromToolArgs(args);
6020
- if (!token) {
6021
- return false;
6022
- }
6023
- const listener = toolCallProgressListeners.get(token);
6024
- if (!listener) {
6025
- return false;
6026
- }
6027
- listener(update);
6028
- return true;
6029
- }
6030
- /**
6031
- * Note: [💞] Ignore a discrepancy between file name and entity name
6032
- */
6033
-
6034
- /**
6035
- * Marker property stored inside serialized tool-execution envelopes.
6036
- *
6037
- * @private internal tool-execution transport
6038
- */
6039
- const TOOL_EXECUTION_ENVELOPE_MARKER = '__promptbookToolExecutionEnvelope';
6040
- /**
6041
- * Creates one serialized tool-execution envelope.
6042
- *
6043
- * @private internal tool-execution transport
6044
- */
6045
- function createToolExecutionEnvelope(options) {
6046
- const envelope = {
6047
- [TOOL_EXECUTION_ENVELOPE_MARKER]: true,
6048
- assistantMessage: options.assistantMessage,
6049
- toolResult: options.toolResult,
6050
- };
6051
- return JSON.stringify(envelope);
6052
- }
6053
- /**
6054
- * Parses one serialized tool-execution envelope when present.
6055
- *
6056
- * @private internal tool-execution transport
6057
- */
6058
- function parseToolExecutionEnvelope(rawValue) {
6059
- if (typeof rawValue !== 'string') {
6060
- return null;
6061
- }
6062
- try {
6063
- const parsedValue = JSON.parse(rawValue);
6064
- if (!parsedValue ||
6065
- typeof parsedValue !== 'object' ||
6066
- parsedValue[TOOL_EXECUTION_ENVELOPE_MARKER] !== true ||
6067
- typeof parsedValue.assistantMessage !== 'string') {
6068
- return null;
6069
- }
6070
- return {
6071
- assistantMessage: parsedValue.assistantMessage,
6072
- toolResult: parsedValue.toolResult,
6073
- };
6074
- }
6075
- catch (_a) {
6076
- return null;
6077
- }
6078
- }
6079
- /**
6080
- * Note: [💞] Ignore a discrepancy between file name and entity name
6081
- */
6082
-
6083
5918
  /**
6084
5919
  * Normalizes a given text to camelCase format.
6085
5920
  *
@@ -6296,7 +6131,7 @@ function serializeError(error) {
6296
6131
  const { name, message, stack } = error;
6297
6132
  const { id } = error;
6298
6133
  if (!Object.keys(ALL_ERRORS).includes(name)) {
6299
- console.error(spaceTrim$2((block) => `
6134
+ console.error(spaceTrim$1((block) => `
6300
6135
 
6301
6136
  Cannot serialize error with name "${name}"
6302
6137
 
@@ -6402,7 +6237,7 @@ function jsonParse(value) {
6402
6237
  }
6403
6238
  else if (typeof value !== 'string') {
6404
6239
  console.error('Can not parse JSON from non-string value.', { text: value });
6405
- throw new Error(spaceTrim$2(`
6240
+ throw new Error(spaceTrim$1(`
6406
6241
  Can not parse JSON from non-string value.
6407
6242
 
6408
6243
  The value type: ${typeof value}
@@ -6416,7 +6251,7 @@ function jsonParse(value) {
6416
6251
  if (!(error instanceof Error)) {
6417
6252
  throw error;
6418
6253
  }
6419
- throw new Error(spaceTrim$2((block) => `
6254
+ throw new Error(spaceTrim$1((block) => `
6420
6255
  ${block(error.message)}
6421
6256
 
6422
6257
  The expected JSON text:
@@ -6705,7 +6540,7 @@ function buildParametersSection(items) {
6705
6540
  const entries = items
6706
6541
  .flatMap((item) => formatParameterListItem(item).split(/\r?\n/))
6707
6542
  .filter((line) => line !== '');
6708
- return spaceTrim$2((block) => `
6543
+ return spaceTrim$1((block) => `
6709
6544
  **Parameters:**
6710
6545
  ${block(entries.join('\n'))}
6711
6546
 
@@ -6778,7 +6613,7 @@ function isPromptString(value) {
6778
6613
  */
6779
6614
  function prompt(strings, ...values) {
6780
6615
  if (values.length === 0) {
6781
- return new PromptString(spaceTrim$2(strings.join('')));
6616
+ return new PromptString(spaceTrim$1(strings.join('')));
6782
6617
  }
6783
6618
  const stringsWithHiddenParameters = strings.map((stringsItem) => ParameterEscaping.hideBrackets(stringsItem));
6784
6619
  const parameterMetadata = values.map((value) => {
@@ -6819,7 +6654,7 @@ function prompt(strings, ...values) {
6819
6654
  ? `${result}${stringsItem}`
6820
6655
  : `${result}${stringsItem}${ParameterSection.formatParameterPlaceholder(parameterName)}`;
6821
6656
  }, '');
6822
- pipelineString = spaceTrim$2(pipelineString);
6657
+ pipelineString = spaceTrim$1(pipelineString);
6823
6658
  try {
6824
6659
  pipelineString = templateParameters(pipelineString, parameters);
6825
6660
  }
@@ -6828,7 +6663,7 @@ function prompt(strings, ...values) {
6828
6663
  throw error;
6829
6664
  }
6830
6665
  console.error({ pipelineString, parameters, parameterNames: parameterNamesOrdered, error });
6831
- throw new UnexpectedError(spaceTrim$2((block) => `
6666
+ throw new UnexpectedError(spaceTrim$1((block) => `
6832
6667
  Internal error in prompt template literal
6833
6668
 
6834
6669
  ${block(JSON.stringify({ strings, values }, null, 4))}}
@@ -6946,7 +6781,7 @@ const CountUtils = {
6946
6781
  * @public exported from `@promptbook/utils`
6947
6782
  */
6948
6783
  function computeHash(value) {
6949
- return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
6784
+ return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
6950
6785
  }
6951
6786
  /**
6952
6787
  * TODO: [🥬][🥬] Use this ACRY
@@ -9001,6 +8836,159 @@ function isValidPipelineUrl(url) {
9001
8836
  * TODO: [🐠] Maybe more info why the URL is invalid
9002
8837
  */
9003
8838
 
8839
+ /**
8840
+ * Marker property stored inside serialized tool-execution envelopes.
8841
+ *
8842
+ * @private internal tool-execution transport
8843
+ */
8844
+ const TOOL_EXECUTION_ENVELOPE_MARKER = '__promptbookToolExecutionEnvelope';
8845
+ /**
8846
+ * Creates one serialized tool-execution envelope.
8847
+ *
8848
+ * @private internal tool-execution transport
8849
+ */
8850
+ function createToolExecutionEnvelope(options) {
8851
+ const envelope = {
8852
+ [TOOL_EXECUTION_ENVELOPE_MARKER]: true,
8853
+ assistantMessage: options.assistantMessage,
8854
+ toolResult: options.toolResult,
8855
+ };
8856
+ return JSON.stringify(envelope);
8857
+ }
8858
+ /**
8859
+ * Parses one serialized tool-execution envelope when present.
8860
+ *
8861
+ * @private internal tool-execution transport
8862
+ */
8863
+ function parseToolExecutionEnvelope(rawValue) {
8864
+ if (typeof rawValue !== 'string') {
8865
+ return null;
8866
+ }
8867
+ try {
8868
+ const parsedValue = JSON.parse(rawValue);
8869
+ if (!parsedValue ||
8870
+ typeof parsedValue !== 'object' ||
8871
+ parsedValue[TOOL_EXECUTION_ENVELOPE_MARKER] !== true ||
8872
+ typeof parsedValue.assistantMessage !== 'string') {
8873
+ return null;
8874
+ }
8875
+ return {
8876
+ assistantMessage: parsedValue.assistantMessage,
8877
+ toolResult: parsedValue.toolResult,
8878
+ };
8879
+ }
8880
+ catch (_a) {
8881
+ return null;
8882
+ }
8883
+ }
8884
+ /**
8885
+ * Note: [💞] Ignore a discrepancy between file name and entity name
8886
+ */
8887
+
8888
+ /**
8889
+ * Prompt parameter key used to pass hidden runtime context to tool execution.
8890
+ *
8891
+ * @private internal runtime wiring for commitment tools
8892
+ */
8893
+ const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
8894
+ /**
8895
+ * Hidden argument key used to pass runtime context into individual tool calls.
8896
+ *
8897
+ * @private internal runtime wiring for commitment tools
8898
+ */
8899
+ const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
8900
+ /**
8901
+ * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
8902
+ *
8903
+ * @private internal runtime wiring for commitment tools
8904
+ */
8905
+ const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
8906
+ /**
8907
+ * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
8908
+ *
8909
+ * @private internal runtime wiring for commitment tools
8910
+ */
8911
+ const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
8912
+ /**
8913
+ * Monotonic counter used for hidden progress-listener tokens.
8914
+ *
8915
+ * @private internal runtime wiring for commitment tools
8916
+ */
8917
+ let toolCallProgressListenerCounter = 0;
8918
+ /**
8919
+ * Active tool-progress listeners keyed by hidden execution token.
8920
+ *
8921
+ * @private internal runtime wiring for commitment tools
8922
+ */
8923
+ const toolCallProgressListeners = new Map();
8924
+ /**
8925
+ * Parses unknown runtime context payload into a normalized object.
8926
+ *
8927
+ * @private internal runtime wiring for commitment tools
8928
+ */
8929
+ function parseToolRuntimeContext(rawValue) {
8930
+ if (!rawValue) {
8931
+ return null;
8932
+ }
8933
+ let parsed = rawValue;
8934
+ if (typeof rawValue === 'string') {
8935
+ try {
8936
+ parsed = JSON.parse(rawValue);
8937
+ }
8938
+ catch (_a) {
8939
+ return null;
8940
+ }
8941
+ }
8942
+ if (!parsed || typeof parsed !== 'object') {
8943
+ return null;
8944
+ }
8945
+ return parsed;
8946
+ }
8947
+ /**
8948
+ * Reads runtime context attached to tool call arguments.
8949
+ *
8950
+ * @private internal runtime wiring for commitment tools
8951
+ */
8952
+ function readToolRuntimeContextFromToolArgs(args) {
8953
+ return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
8954
+ }
8955
+ /**
8956
+ * Serializes runtime context for prompt parameters.
8957
+ *
8958
+ * @private internal runtime wiring for commitment tools
8959
+ */
8960
+ function serializeToolRuntimeContext(context) {
8961
+ return JSON.stringify(context);
8962
+ }
8963
+ /**
8964
+ * Registers one in-memory listener that receives progress updates emitted by a running tool.
8965
+ *
8966
+ * The returned token is passed into script execution as a hidden argument so tool implementations
8967
+ * can stream progress without exposing extra parameters to the model.
8968
+ *
8969
+ * @param listener - Listener notified about tool progress.
8970
+ * @returns Hidden token used to route progress updates.
8971
+ * @private internal runtime wiring for commitment tools
8972
+ */
8973
+ function registerToolCallProgressListener(listener) {
8974
+ toolCallProgressListenerCounter += 1;
8975
+ const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
8976
+ toolCallProgressListeners.set(token, listener);
8977
+ return token;
8978
+ }
8979
+ /**
8980
+ * Unregisters one in-memory progress listener.
8981
+ *
8982
+ * @param token - Token previously created by `registerToolCallProgressListener`.
8983
+ * @private internal runtime wiring for commitment tools
8984
+ */
8985
+ function unregisterToolCallProgressListener(token) {
8986
+ toolCallProgressListeners.delete(token);
8987
+ }
8988
+ /**
8989
+ * Note: [💞] Ignore a discrepancy between file name and entity name
8990
+ */
8991
+
9004
8992
  /**
9005
8993
  * Function `addUsage` will add multiple usages into one
9006
8994
  *
@@ -9055,53 +9043,6 @@ function addUsage(...usageItems) {
9055
9043
  }, deepClone(ZERO_USAGE));
9056
9044
  }
9057
9045
 
9058
- /**
9059
- * Maps Promptbook tools to OpenAI tools.
9060
- *
9061
- * @private
9062
- */
9063
- function mapToolsToOpenAi(tools) {
9064
- return tools.map((tool) => ({
9065
- type: 'function',
9066
- function: {
9067
- name: tool.name,
9068
- description: tool.description,
9069
- parameters: tool.parameters,
9070
- },
9071
- }));
9072
- }
9073
-
9074
- /**
9075
- * Builds a tool invocation script that injects hidden runtime context into tool args.
9076
- *
9077
- * @private utility of OpenAI tool execution wrappers
9078
- */
9079
- function buildToolInvocationScript(options) {
9080
- const { functionName, functionArgsExpression } = options;
9081
- return `
9082
- const args = ${functionArgsExpression};
9083
- const runtimeContextRaw =
9084
- typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
9085
- ? undefined
9086
- : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
9087
-
9088
- if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
9089
- args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
9090
- }
9091
-
9092
- const toolProgressTokenRaw =
9093
- typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
9094
- ? undefined
9095
- : ${TOOL_PROGRESS_TOKEN_PARAMETER};
9096
-
9097
- if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
9098
- args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
9099
- }
9100
-
9101
- return await ${functionName}(args);
9102
- `;
9103
- }
9104
-
9105
9046
  /**
9106
9047
  * Parses an OpenAI error message to identify which parameter is unsupported
9107
9048
  *
@@ -9158,6 +9099,53 @@ function isUnsupportedParameterError(error) {
9158
9099
  errorMessage.includes('does not support'));
9159
9100
  }
9160
9101
 
9102
+ /**
9103
+ * Builds a tool invocation script that injects hidden runtime context into tool args.
9104
+ *
9105
+ * @private utility of OpenAI tool execution wrappers
9106
+ */
9107
+ function buildToolInvocationScript(options) {
9108
+ const { functionName, functionArgsExpression } = options;
9109
+ return `
9110
+ const args = ${functionArgsExpression};
9111
+ const runtimeContextRaw =
9112
+ typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
9113
+ ? undefined
9114
+ : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
9115
+
9116
+ if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
9117
+ args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
9118
+ }
9119
+
9120
+ const toolProgressTokenRaw =
9121
+ typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
9122
+ ? undefined
9123
+ : ${TOOL_PROGRESS_TOKEN_PARAMETER};
9124
+
9125
+ if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
9126
+ args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
9127
+ }
9128
+
9129
+ return await ${functionName}(args);
9130
+ `;
9131
+ }
9132
+
9133
+ /**
9134
+ * Maps Promptbook tools to OpenAI tools.
9135
+ *
9136
+ * @private
9137
+ */
9138
+ function mapToolsToOpenAi(tools) {
9139
+ return tools.map((tool) => ({
9140
+ type: 'function',
9141
+ function: {
9142
+ name: tool.name,
9143
+ description: tool.description,
9144
+ parameters: tool.parameters,
9145
+ },
9146
+ }));
9147
+ }
9148
+
9161
9149
  /**
9162
9150
  * Provides access to the structured clone implementation when available.
9163
9151
  */
@@ -10124,7 +10112,7 @@ class OpenAiCompatibleExecutionTools {
10124
10112
  // Note: Match exact or prefix for model families
10125
10113
  const model = this.HARDCODED_MODELS.find(({ modelName }) => modelName === defaultModelName || modelName.startsWith(defaultModelName));
10126
10114
  if (model === undefined) {
10127
- throw new PipelineExecutionError(spaceTrim$2((block) => `
10115
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
10128
10116
  Cannot find model in ${this.title} models with name "${defaultModelName}" which should be used as default.
10129
10117
 
10130
10118
  Available models:
@@ -11999,7 +11987,7 @@ class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
11999
11987
  assertsError(error);
12000
11988
  const serializedError = serializeError(error);
12001
11989
  errors = [serializedError];
12002
- functionResponse = spaceTrim$2((block) => `
11990
+ functionResponse = spaceTrim$1((block) => `
12003
11991
 
12004
11992
  The invoked tool \`${functionName}\` failed with error:
12005
11993
 
@@ -13088,7 +13076,7 @@ function pipelineJsonToString(pipelineJson) {
13088
13076
  pipelineString += '\n\n';
13089
13077
  pipelineString += '```' + contentLanguage;
13090
13078
  pipelineString += '\n';
13091
- pipelineString += spaceTrim$2(content);
13079
+ pipelineString += spaceTrim$1(content);
13092
13080
  // <- TODO: [main] !!3 Escape
13093
13081
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
13094
13082
  pipelineString += '\n';
@@ -13988,14 +13976,14 @@ class MultipleLlmExecutionTools {
13988
13976
  if (description === undefined) {
13989
13977
  return headLine;
13990
13978
  }
13991
- return spaceTrim$2((block) => `
13979
+ return spaceTrim$1((block) => `
13992
13980
  ${headLine}
13993
13981
 
13994
13982
  ${ /* <- Note: Indenting the description: */block(description)}
13995
13983
  `);
13996
13984
  })
13997
13985
  .join('\n\n');
13998
- return spaceTrim$2((block) => `
13986
+ return spaceTrim$1((block) => `
13999
13987
  Multiple LLM Providers:
14000
13988
 
14001
13989
  ${block(innerModelsTitlesAndDescriptions)}
@@ -14097,7 +14085,7 @@ class MultipleLlmExecutionTools {
14097
14085
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
14098
14086
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
14099
14087
  // 3) ...
14100
- spaceTrim$2((block) => `
14088
+ spaceTrim$1((block) => `
14101
14089
  All execution tools of ${this.title} failed:
14102
14090
 
14103
14091
  ${block(errors
@@ -14110,7 +14098,7 @@ class MultipleLlmExecutionTools {
14110
14098
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\` into ${this.title}`);
14111
14099
  }
14112
14100
  else {
14113
- throw new PipelineExecutionError(spaceTrim$2((block) => `
14101
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
14114
14102
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}" into ${this.title}
14115
14103
 
14116
14104
  Available \`LlmExecutionTools\`:
@@ -14147,7 +14135,7 @@ class MultipleLlmExecutionTools {
14147
14135
  */
14148
14136
  function joinLlmExecutionTools(title, ...llmExecutionTools) {
14149
14137
  if (llmExecutionTools.length === 0) {
14150
- const warningMessage = spaceTrim$2(`
14138
+ const warningMessage = spaceTrim$1(`
14151
14139
  You have not provided any \`LlmExecutionTools\`
14152
14140
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
14153
14141
 
@@ -14319,14 +14307,14 @@ function $registeredScrapersMessage(availableScrapers) {
14319
14307
  return { ...metadata, isMetadataAviailable, isInstalled, isAvailableInTools };
14320
14308
  });
14321
14309
  if (metadata.length === 0) {
14322
- return spaceTrim$2(`
14310
+ return spaceTrim$1(`
14323
14311
  **No scrapers are available**
14324
14312
 
14325
14313
  This is a unexpected behavior, you are probably using some broken version of Promptbook
14326
14314
  At least there should be available the metadata of the scrapers
14327
14315
  `);
14328
14316
  }
14329
- return spaceTrim$2((block) => `
14317
+ return spaceTrim$1((block) => `
14330
14318
  Available scrapers are:
14331
14319
  ${block(metadata
14332
14320
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvailableInBrowser, isAvailableInTools, }, i) => {
@@ -14427,7 +14415,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
14427
14415
  else if (urlOrRequest instanceof Request) {
14428
14416
  url = urlOrRequest.url;
14429
14417
  }
14430
- throw new PromptbookFetchError(spaceTrim$2((block) => `
14418
+ throw new PromptbookFetchError(spaceTrim$1((block) => `
14431
14419
  Can not fetch "${url}"
14432
14420
 
14433
14421
  Fetch error:
@@ -14587,7 +14575,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
14587
14575
  const fileExtension = getFileExtension(filename);
14588
14576
  const mimeType = extensionToMimeType(fileExtension || '');
14589
14577
  if (!(await isFileExisting(filename, tools.fs))) {
14590
- throw new NotFoundError(spaceTrim$2((block) => `
14578
+ throw new NotFoundError(spaceTrim$1((block) => `
14591
14579
  Can not make source handler for file which does not exist:
14592
14580
 
14593
14581
  File:
@@ -14680,7 +14668,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
14680
14668
  // <- TODO: [🪓] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
14681
14669
  break;
14682
14670
  }
14683
- console.warn(spaceTrim$2((block) => `
14671
+ console.warn(spaceTrim$1((block) => `
14684
14672
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
14685
14673
 
14686
14674
  The source:
@@ -14696,7 +14684,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
14696
14684
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
14697
14685
  }
14698
14686
  if (partialPieces === null) {
14699
- throw new KnowledgeScrapeError(spaceTrim$2((block) => `
14687
+ throw new KnowledgeScrapeError(spaceTrim$1((block) => `
14700
14688
  Cannot scrape knowledge
14701
14689
 
14702
14690
  The source:
@@ -15133,7 +15121,7 @@ const CsvFormatParser = {
15133
15121
  const { value, outputParameterName, settings, mapCallback, onProgress } = options;
15134
15122
  const csv = csvParse(value, settings);
15135
15123
  if (csv.errors.length !== 0) {
15136
- throw new CsvFormatError(spaceTrim$2((block) => `
15124
+ throw new CsvFormatError(spaceTrim$1((block) => `
15137
15125
  CSV parsing error
15138
15126
 
15139
15127
  Error(s) from CSV parsing:
@@ -15178,7 +15166,7 @@ const CsvFormatParser = {
15178
15166
  const { value, settings, mapCallback, onProgress } = options;
15179
15167
  const csv = csvParse(value, settings);
15180
15168
  if (csv.errors.length !== 0) {
15181
- throw new CsvFormatError(spaceTrim$2((block) => `
15169
+ throw new CsvFormatError(spaceTrim$1((block) => `
15182
15170
  CSV parsing error
15183
15171
 
15184
15172
  Error(s) from CSV parsing:
@@ -15364,7 +15352,7 @@ function mapAvailableToExpectedParameters(options) {
15364
15352
  }
15365
15353
  // Phase 2️⃣: Non-matching mapping
15366
15354
  if (expectedParameterNames.size !== availableParametersNames.size) {
15367
- throw new PipelineExecutionError(spaceTrim$2((block) => `
15355
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
15368
15356
  Can not map available parameters to expected parameters
15369
15357
 
15370
15358
  Mapped parameters:
@@ -15937,7 +15925,7 @@ async function executeFormatSubvalues(options) {
15937
15925
  return /* not await */ executeAttempts({ ...options, logLlmCall });
15938
15926
  }
15939
15927
  if (jokerParameterNames.length !== 0) {
15940
- throw new UnexpectedError(spaceTrim$2((block) => `
15928
+ throw new UnexpectedError(spaceTrim$1((block) => `
15941
15929
  JOKER parameters are not supported together with FOREACH command
15942
15930
 
15943
15931
  [🧞‍♀️] This should be prevented in \`validatePipeline\`
@@ -15950,7 +15938,7 @@ async function executeFormatSubvalues(options) {
15950
15938
  if (formatDefinition === undefined) {
15951
15939
  throw new UnexpectedError(
15952
15940
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
15953
- spaceTrim$2((block) => `
15941
+ spaceTrim$1((block) => `
15954
15942
  Unsupported format "${task.foreach.formatName}"
15955
15943
 
15956
15944
  Available formats:
@@ -15967,7 +15955,7 @@ async function executeFormatSubvalues(options) {
15967
15955
  if (subvalueParser === undefined) {
15968
15956
  throw new UnexpectedError(
15969
15957
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
15970
- spaceTrim$2((block) => `
15958
+ spaceTrim$1((block) => `
15971
15959
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
15972
15960
 
15973
15961
  Available subformat names for format "${formatDefinition.formatName}":
@@ -16007,7 +15995,7 @@ async function executeFormatSubvalues(options) {
16007
15995
  if (!(error instanceof PipelineExecutionError)) {
16008
15996
  throw error;
16009
15997
  }
16010
- const highLevelError = new PipelineExecutionError(spaceTrim$2((block) => `
15998
+ const highLevelError = new PipelineExecutionError(spaceTrim$1((block) => `
16011
15999
  ${error.message}
16012
16000
 
16013
16001
  This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -16031,7 +16019,7 @@ async function executeFormatSubvalues(options) {
16031
16019
  ...options,
16032
16020
  priority: priority + index,
16033
16021
  parameters: allSubparameters,
16034
- pipelineIdentification: spaceTrim$2((block) => `
16022
+ pipelineIdentification: spaceTrim$1((block) => `
16035
16023
  ${block(pipelineIdentification)}
16036
16024
  Subparameter index: ${index}
16037
16025
  `),
@@ -16040,7 +16028,7 @@ async function executeFormatSubvalues(options) {
16040
16028
  }
16041
16029
  catch (error) {
16042
16030
  if (length > BIG_DATASET_TRESHOLD) {
16043
- console.error(spaceTrim$2((block) => `
16031
+ console.error(spaceTrim$1((block) => `
16044
16032
  ${error.message}
16045
16033
 
16046
16034
  This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -16912,8 +16900,8 @@ class MarkdownScraper {
16912
16900
  knowledgeTextPieces.map(async (knowledgeTextPiece, i) => {
16913
16901
  // Note: These are just default values, they will be overwritten by the actual values:
16914
16902
  let name = `piece-${i}`;
16915
- let title = spaceTrim$2(knowledgeTextPiece.substring(0, 100));
16916
- const knowledgePieceContent = spaceTrim$2(knowledgeTextPiece);
16903
+ let title = spaceTrim$1(knowledgeTextPiece.substring(0, 100));
16904
+ const knowledgePieceContent = spaceTrim$1(knowledgeTextPiece);
16917
16905
  let keywords = [];
16918
16906
  const index = [];
16919
16907
  /*
@@ -16926,7 +16914,7 @@ class MarkdownScraper {
16926
16914
  isCrashedOnError: true,
16927
16915
  });
16928
16916
  const { title: titleRaw = 'Untitled' } = titleResult.outputParameters;
16929
- title = spaceTrim$2(titleRaw) /* <- TODO: Maybe do in pipeline */;
16917
+ title = spaceTrim$1(titleRaw) /* <- TODO: Maybe do in pipeline */;
16930
16918
  name = titleToName(title);
16931
16919
  // --- Keywords
16932
16920
  const keywordsResult = await prepareKeywordsExecutor({ knowledgePieceContent }).asPromise({
@@ -17081,7 +17069,7 @@ class BoilerplateScraper {
17081
17069
  await $execCommand(command);
17082
17070
  // Note: [0]
17083
17071
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
17084
- throw new UnexpectedError(spaceTrim$2((block) => `
17072
+ throw new UnexpectedError(spaceTrim$1((block) => `
17085
17073
  File that was supposed to be created by Pandoc does not exist for unknown reason
17086
17074
 
17087
17075
  Expected file:
@@ -17245,7 +17233,7 @@ class DocumentScraper {
17245
17233
  await $execCommand(command);
17246
17234
  // Note: [0]
17247
17235
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
17248
- throw new UnexpectedError(spaceTrim$2((block) => `
17236
+ throw new UnexpectedError(spaceTrim$1((block) => `
17249
17237
  File that was supposed to be created by Pandoc does not exist for unknown reason
17250
17238
 
17251
17239
  Expected file:
@@ -17396,7 +17384,7 @@ class LegacyDocumentScraper {
17396
17384
  await $execCommand(command);
17397
17385
  const files = await readdir(documentSourceOutdirPathForLibreOffice);
17398
17386
  if (files.length !== 1) {
17399
- throw new UnexpectedError(spaceTrim$2((block) => `
17387
+ throw new UnexpectedError(spaceTrim$1((block) => `
17400
17388
  Expected exactly 1 file in the LibreOffice output directory, got ${files.length}
17401
17389
 
17402
17390
  The temporary folder:
@@ -17410,7 +17398,7 @@ class LegacyDocumentScraper {
17410
17398
  await rename(join(documentSourceOutdirPathForLibreOffice, file), cacheFilehandler.filename);
17411
17399
  await rmdir(documentSourceOutdirPathForLibreOffice);
17412
17400
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
17413
- throw new UnexpectedError(spaceTrim$2((block) => `
17401
+ throw new UnexpectedError(spaceTrim$1((block) => `
17414
17402
  File that was supposed to be created by LibreOffice does not exist for unknown reason
17415
17403
 
17416
17404
  Expected file:
@@ -27486,9 +27474,9 @@ function createTimeoutSystemMessage(extraInstructions) {
27486
27474
  return spaceTrim$1((block) => `
27487
27475
  Timeout scheduling:
27488
27476
  - Use "set_timeout" to wake this same chat thread in the future.
27489
- - Timers are thread-scoped, not global for the whole agent.
27477
+ - Use "list_timeouts" to review timeouts across all chats for the same user+agent scope.
27478
+ - "cancel_timeout" accepts a timeout id from any chat in this same user+agent scope.
27490
27479
  - When one timeout elapses, you will receive a new user-like message that explicitly says it is a timeout wake-up and includes the \`timeoutId\`.
27491
- - Use "cancel_timeout" when a previously scheduled timeout is no longer relevant.
27492
27480
  - Do not claim a timer was set or cancelled unless the tool confirms it.
27493
27481
  ${block(extraInstructions)}
27494
27482
  `);
@@ -27500,13 +27488,6 @@ function createTimeoutSystemMessage(extraInstructions) {
27500
27488
  * @private internal utility of USE TIMEOUT
27501
27489
  */
27502
27490
  function createDisabledTimeoutResult(action, message) {
27503
- if (action === 'set') {
27504
- return {
27505
- action,
27506
- status: 'disabled',
27507
- message,
27508
- };
27509
- }
27510
27491
  return {
27511
27492
  action,
27512
27493
  status: 'disabled',
@@ -27533,6 +27514,18 @@ function getTimeoutToolRuntimeAdapterOrDisabledResult(action, runtimeContext) {
27533
27514
  }
27534
27515
  }
27535
27516
 
27517
+ /**
27518
+ * Default number of rows returned by `list_timeouts`.
27519
+ *
27520
+ * @private internal USE TIMEOUT constant
27521
+ */
27522
+ const DEFAULT_LIST_TIMEOUTS_LIMIT = 20;
27523
+ /**
27524
+ * Hard cap for `list_timeouts` page size.
27525
+ *
27526
+ * @private internal USE TIMEOUT constant
27527
+ */
27528
+ const MAX_LIST_TIMEOUTS_LIMIT = 100;
27536
27529
  /**
27537
27530
  * Parses and validates `USE TIMEOUT` tool arguments.
27538
27531
  *
@@ -27567,6 +27560,31 @@ const parseTimeoutToolArgs = {
27567
27560
  }
27568
27561
  return { timeoutId };
27569
27562
  },
27563
+ /**
27564
+ * Parses `list_timeouts` input.
27565
+ */
27566
+ list(args) {
27567
+ if (args.includeFinished !== undefined && typeof args.includeFinished !== 'boolean') {
27568
+ throw new PipelineExecutionError(spaceTrim$1(`
27569
+ Timeout \`includeFinished\` must be a boolean when provided.
27570
+ `));
27571
+ }
27572
+ const parsedLimit = args.limit === undefined ? DEFAULT_LIST_TIMEOUTS_LIMIT : Math.floor(Number(args.limit));
27573
+ if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
27574
+ throw new PipelineExecutionError(spaceTrim$1(`
27575
+ Timeout \`limit\` must be a positive number.
27576
+ `));
27577
+ }
27578
+ if (parsedLimit > MAX_LIST_TIMEOUTS_LIMIT) {
27579
+ throw new PipelineExecutionError(spaceTrim$1(`
27580
+ Timeout \`limit\` must be at most \`${MAX_LIST_TIMEOUTS_LIMIT}\`.
27581
+ `));
27582
+ }
27583
+ return {
27584
+ includeFinished: args.includeFinished === true,
27585
+ limit: parsedLimit,
27586
+ };
27587
+ },
27570
27588
  };
27571
27589
 
27572
27590
  /**
@@ -27577,6 +27595,7 @@ const parseTimeoutToolArgs = {
27577
27595
  const TimeoutToolNames = {
27578
27596
  set: 'set_timeout',
27579
27597
  cancel: 'cancel_timeout',
27598
+ list: 'list_timeouts',
27580
27599
  };
27581
27600
 
27582
27601
  /**
@@ -27676,6 +27695,35 @@ function createTimeoutToolFunctions() {
27676
27695
  return JSON.stringify(result);
27677
27696
  }
27678
27697
  },
27698
+ async [TimeoutToolNames.list](args) {
27699
+ const runtimeContext = resolveTimeoutRuntimeContext(args);
27700
+ const { adapter, disabledResult } = getTimeoutToolRuntimeAdapterOrDisabledResult('list', runtimeContext);
27701
+ if (!adapter || disabledResult) {
27702
+ return JSON.stringify(disabledResult);
27703
+ }
27704
+ try {
27705
+ const parsedArgs = parseTimeoutToolArgs.list(args);
27706
+ const listedTimeouts = await adapter.listTimeouts(parsedArgs, runtimeContext);
27707
+ const result = {
27708
+ action: 'list',
27709
+ status: 'listed',
27710
+ items: listedTimeouts.items,
27711
+ total: listedTimeouts.total,
27712
+ };
27713
+ return createToolExecutionEnvelope({
27714
+ assistantMessage: listedTimeouts.total === 1 ? 'Found 1 timeout.' : `Found ${listedTimeouts.total} timeouts.`,
27715
+ toolResult: result,
27716
+ });
27717
+ }
27718
+ catch (error) {
27719
+ const result = {
27720
+ action: 'list',
27721
+ status: 'error',
27722
+ message: error instanceof Error ? error.message : String(error),
27723
+ };
27724
+ return JSON.stringify(result);
27725
+ }
27726
+ },
27679
27727
  };
27680
27728
  }
27681
27729
 
@@ -27709,26 +27757,45 @@ function createTimeoutTools(existingTools = []) {
27709
27757
  if (!tools.some((tool) => tool.name === TimeoutToolNames.cancel)) {
27710
27758
  tools.push({
27711
27759
  name: TimeoutToolNames.cancel,
27712
- description: 'Cancel one previously scheduled timeout in the current chat thread.',
27760
+ description: 'Cancel one previously scheduled timeout within the same user+agent scope, even if it was set in another chat.',
27713
27761
  parameters: {
27714
27762
  type: 'object',
27715
27763
  properties: {
27716
27764
  timeoutId: {
27717
27765
  type: 'string',
27718
- description: 'Identifier returned earlier by `set_timeout`.',
27766
+ description: 'Identifier returned earlier by `set_timeout` or `list_timeouts`.',
27719
27767
  },
27720
27768
  },
27721
27769
  required: ['timeoutId'],
27722
27770
  },
27723
27771
  });
27724
27772
  }
27773
+ if (!tools.some((tool) => tool.name === TimeoutToolNames.list)) {
27774
+ tools.push({
27775
+ name: TimeoutToolNames.list,
27776
+ description: 'List scheduled timeouts across all chats for this same user+agent scope so they can be reviewed or cancelled.',
27777
+ parameters: {
27778
+ type: 'object',
27779
+ properties: {
27780
+ includeFinished: {
27781
+ type: 'boolean',
27782
+ description: 'When true, include completed, failed, and cancelled rows in addition to active timeouts.',
27783
+ },
27784
+ limit: {
27785
+ type: 'number',
27786
+ description: 'Maximum number of rows to return (default 20, max 100).',
27787
+ },
27788
+ },
27789
+ },
27790
+ });
27791
+ }
27725
27792
  return tools;
27726
27793
  }
27727
27794
 
27728
27795
  /**
27729
27796
  * `USE TIMEOUT` commitment definition.
27730
27797
  *
27731
- * The `USE TIMEOUT` commitment enables thread-scoped timers that wake the same chat later.
27798
+ * The `USE TIMEOUT` commitment enables timeout wake-ups and scoped timeout management.
27732
27799
  *
27733
27800
  * @private [🪔] Maybe export the commitments through some package
27734
27801
  */
@@ -27743,7 +27810,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
27743
27810
  * Short one-line description of `USE TIMEOUT`.
27744
27811
  */
27745
27812
  get description() {
27746
- return 'Enable thread-scoped timers that can wake the same chat in the future.';
27813
+ return 'Enable timeout wake-ups plus scoped timeout listing/cancellation across chats.';
27747
27814
  }
27748
27815
  /**
27749
27816
  * Icon for this commitment.
@@ -27758,14 +27825,15 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
27758
27825
  return spaceTrim$1(`
27759
27826
  # USE TIMEOUT
27760
27827
 
27761
- Enables the agent to schedule thread-scoped timeout wake-ups.
27828
+ Enables timeout wake-ups and timeout management for the same user+agent scope.
27762
27829
 
27763
27830
  ## Key aspects
27764
27831
 
27765
27832
  - The agent uses \`set_timeout\` to schedule a future wake-up in the same chat thread.
27766
27833
  - The tool returns immediately while the timeout is stored and executed by the runtime later.
27767
27834
  - The wake-up arrives as a new user-like timeout message in the same conversation.
27768
- - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`.
27835
+ - The agent can inspect known timeouts via \`list_timeouts\`.
27836
+ - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`, including timeouts created in another chat.
27769
27837
  - Commitment content is treated as optional timeout policy instructions.
27770
27838
 
27771
27839
  ## Examples
@@ -27794,6 +27862,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
27794
27862
  return {
27795
27863
  [TimeoutToolNames.set]: 'Set timer',
27796
27864
  [TimeoutToolNames.cancel]: 'Cancel timer',
27865
+ [TimeoutToolNames.list]: 'List timers',
27797
27866
  };
27798
27867
  }
27799
27868
  /**
@@ -29206,7 +29275,7 @@ function computeAgentHash(agentSource) {
29206
29275
  * @public exported from `@promptbook/core`
29207
29276
  */
29208
29277
  function normalizeAgentName(rawAgentName) {
29209
- return titleToName(spaceTrim$2(rawAgentName));
29278
+ return titleToName(spaceTrim$1(rawAgentName));
29210
29279
  }
29211
29280
 
29212
29281
  /**
@@ -29381,7 +29450,7 @@ function parseAgentSource(agentSource) {
29381
29450
  continue;
29382
29451
  }
29383
29452
  if (commitment.type === 'FROM') {
29384
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
29453
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
29385
29454
  if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
29386
29455
  continue;
29387
29456
  }
@@ -29404,7 +29473,7 @@ function parseAgentSource(agentSource) {
29404
29473
  continue;
29405
29474
  }
29406
29475
  if (commitment.type === 'IMPORT') {
29407
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
29476
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
29408
29477
  let label = content;
29409
29478
  let iconName = 'ExternalLink'; // Import remote
29410
29479
  try {
@@ -29442,7 +29511,7 @@ function parseAgentSource(agentSource) {
29442
29511
  continue;
29443
29512
  }
29444
29513
  if (commitment.type === 'KNOWLEDGE') {
29445
- const content = spaceTrim$2(commitment.content);
29514
+ const content = spaceTrim$1(commitment.content);
29446
29515
  const extractedUrls = extractUrlsFromText(content);
29447
29516
  let label = content;
29448
29517
  let iconName = 'Book';
@@ -29501,7 +29570,7 @@ function parseAgentSource(agentSource) {
29501
29570
  continue;
29502
29571
  }
29503
29572
  if (commitment.type === 'META LINK') {
29504
- const linkValue = spaceTrim$2(commitment.content);
29573
+ const linkValue = spaceTrim$1(commitment.content);
29505
29574
  links.push(linkValue);
29506
29575
  meta.link = linkValue;
29507
29576
  continue;
@@ -29511,11 +29580,11 @@ function parseAgentSource(agentSource) {
29511
29580
  continue;
29512
29581
  }
29513
29582
  if (commitment.type === 'META IMAGE') {
29514
- meta.image = spaceTrim$2(commitment.content);
29583
+ meta.image = spaceTrim$1(commitment.content);
29515
29584
  continue;
29516
29585
  }
29517
29586
  if (commitment.type === 'META DESCRIPTION') {
29518
- meta.description = spaceTrim$2(commitment.content);
29587
+ meta.description = spaceTrim$1(commitment.content);
29519
29588
  continue;
29520
29589
  }
29521
29590
  if (commitment.type === 'META DISCLAIMER') {
@@ -29523,7 +29592,7 @@ function parseAgentSource(agentSource) {
29523
29592
  continue;
29524
29593
  }
29525
29594
  if (commitment.type === 'META INPUT PLACEHOLDER') {
29526
- meta.inputPlaceholder = spaceTrim$2(commitment.content);
29595
+ meta.inputPlaceholder = spaceTrim$1(commitment.content);
29527
29596
  continue;
29528
29597
  }
29529
29598
  if (commitment.type === 'MESSAGE SUFFIX') {
@@ -29539,7 +29608,7 @@ function parseAgentSource(agentSource) {
29539
29608
  continue;
29540
29609
  }
29541
29610
  if (commitment.type === 'META VOICE') {
29542
- meta.voice = spaceTrim$2(commitment.content);
29611
+ meta.voice = spaceTrim$1(commitment.content);
29543
29612
  continue;
29544
29613
  }
29545
29614
  if (commitment.type !== 'META') {
@@ -29548,10 +29617,10 @@ function parseAgentSource(agentSource) {
29548
29617
  // Parse META commitments - format is "META TYPE content"
29549
29618
  const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
29550
29619
  if (metaTypeRaw === 'LINK') {
29551
- links.push(spaceTrim$2(commitment.content.substring(metaTypeRaw.length)));
29620
+ links.push(spaceTrim$1(commitment.content.substring(metaTypeRaw.length)));
29552
29621
  }
29553
29622
  const metaType = normalizeTo_camelCase(metaTypeRaw);
29554
- meta[metaType] = spaceTrim$2(commitment.content.substring(metaTypeRaw.length));
29623
+ meta[metaType] = spaceTrim$1(commitment.content.substring(metaTypeRaw.length));
29555
29624
  }
29556
29625
  // Generate fullname fallback if no meta fullname specified
29557
29626
  if (!meta.fullname) {
@@ -29582,7 +29651,7 @@ function parseAgentSource(agentSource) {
29582
29651
  * @returns The content with normalized separators
29583
29652
  */
29584
29653
  function normalizeSeparator(content) {
29585
- const trimmed = spaceTrim$2(content);
29654
+ const trimmed = spaceTrim$1(content);
29586
29655
  if (trimmed.includes(',')) {
29587
29656
  return trimmed;
29588
29657
  }
@@ -29595,7 +29664,7 @@ function normalizeSeparator(content) {
29595
29664
  * @returns Normalized domain or a trimmed fallback.
29596
29665
  */
29597
29666
  function normalizeMetaDomain(content) {
29598
- const trimmed = spaceTrim$2(content);
29667
+ const trimmed = spaceTrim$1(content);
29599
29668
  return normalizeDomainForMatching(trimmed) || trimmed.toLowerCase();
29600
29669
  }
29601
29670
  /**
@@ -29743,7 +29812,7 @@ const OpenAiSdkTranspiler = {
29743
29812
  }
29744
29813
  const KNOWLEDGE_THRESHOLD = 1000;
29745
29814
  if (directKnowledge.join('\n').length > KNOWLEDGE_THRESHOLD || knowledgeSources.length > 0) {
29746
- return spaceTrim$2((block) => `
29815
+ return spaceTrim$1((block) => `
29747
29816
  #!/usr/bin/env node
29748
29817
 
29749
29818
  import * as dotenv from 'dotenv';
@@ -29818,7 +29887,7 @@ const OpenAiSdkTranspiler = {
29818
29887
 
29819
29888
  if (context) {
29820
29889
  question = spaceTrim(\`
29821
- ${block(spaceTrim$2(`
29890
+ ${block(spaceTrim$1(`
29822
29891
  Here is some additional context to help you answer the question:
29823
29892
  \${context}
29824
29893
 
@@ -29899,7 +29968,7 @@ const OpenAiSdkTranspiler = {
29899
29968
  })();
29900
29969
  `);
29901
29970
  }
29902
- const source = spaceTrim$2((block) => `
29971
+ const source = spaceTrim$1((block) => `
29903
29972
 
29904
29973
  #!/usr/bin/env node
29905
29974
 
@@ -30176,901 +30245,301 @@ async function fetchUrlContent(url) {
30176
30245
  */
30177
30246
 
30178
30247
  /**
30179
- * Logical public directory marker used in `run_browser` payload paths.
30248
+ * Cached implementation of `run_browser` when it can be resolved.
30180
30249
  *
30181
- * This value is kept stable for UI parsing and `/api/browser-artifacts/*` URL mapping.
30182
- */
30183
- const RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY = '.playwright-cli';
30184
- /**
30185
- * Runtime environment variable that overrides local artifact storage directory.
30186
- */
30187
- const RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV = 'RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY';
30188
- /**
30189
- * Default writable directory for `run_browser` screenshot/video artifacts.
30250
+ * @private internal utility for USE BROWSER commitment
30190
30251
  */
30191
- const DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY = join(tmpdir(), 'promptbook', 'run-browser-artifacts');
30252
+ let cachedRunBrowserTool = null;
30192
30253
  /**
30193
- * Converts Windows separators to POSIX separators for payload paths.
30254
+ * Cached loading error to avoid repeating expensive resolution attempts.
30255
+ *
30256
+ * @private internal utility for USE BROWSER commitment
30194
30257
  */
30195
- function toPosixPath(pathname) {
30196
- return pathname.split('\\').join('/');
30197
- }
30258
+ let cachedRunBrowserToolError = null;
30198
30259
  /**
30199
- * Resolves writable filesystem directory used for artifact persistence.
30260
+ * Attempts to load the server-side `run_browser` tool lazily.
30261
+ *
30262
+ * @returns Loaded `run_browser` implementation
30263
+ * @private internal utility for USE BROWSER commitment
30200
30264
  */
30201
- function resolveRunBrowserArtifactStorageDirectory() {
30202
- const configuredStorageDirectory = process.env[RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV];
30203
- if (configuredStorageDirectory && configuredStorageDirectory.trim()) {
30204
- return configuredStorageDirectory.trim();
30265
+ function loadRunBrowserToolForNode() {
30266
+ if (cachedRunBrowserTool !== null) {
30267
+ return cachedRunBrowserTool;
30205
30268
  }
30206
- return DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY;
30207
- }
30208
- /**
30209
- * Resolves absolute filesystem path of one artifact filename.
30210
- */
30211
- function resolveRunBrowserArtifactFilesystemPath(artifactFilename) {
30212
- return join(resolveRunBrowserArtifactStorageDirectory(), artifactFilename);
30213
- }
30214
- /**
30215
- * Resolves payload path of one artifact filename used by replay renderers.
30216
- */
30217
- function resolveRunBrowserArtifactPublicPath(artifactFilename) {
30218
- return toPosixPath(`${RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY}/${artifactFilename}`);
30219
- }
30220
-
30221
- /**
30222
- * Error code used for remote-browser infrastructure outages.
30223
- */
30224
- const REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE = 'REMOTE_BROWSER_UNAVAILABLE';
30225
- /**
30226
- * Error thrown when a remote Playwright browser cannot be reached.
30227
- */
30228
- class RemoteBrowserUnavailableError extends KnowledgeScrapeError {
30229
- constructor(options) {
30230
- var _a;
30231
- super(options.message);
30232
- this.code = REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
30233
- this.isRetryable = true;
30234
- this.debug = options.debug;
30235
- this.suggestedNextSteps =
30236
- (_a = options.suggestedNextSteps) !== null && _a !== void 0 ? _a : [
30237
- 'Verify remote browser infrastructure is running and reachable from Agents Server.',
30238
- 'Check firewall and DNS routing for the remote browser host and port.',
30239
- 'Retry later or continue with non-graphical fallback scraping.',
30240
- ];
30241
- this.cause = options.cause;
30242
- Object.setPrototypeOf(this, RemoteBrowserUnavailableError.prototype);
30269
+ if (cachedRunBrowserToolError !== null) {
30270
+ throw cachedRunBrowserToolError;
30271
+ }
30272
+ try {
30273
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
30274
+ const runBrowserModule = require('../../../apps/agents-server/src/tools/run_browser');
30275
+ if (typeof runBrowserModule.run_browser !== 'function') {
30276
+ throw new Error('run_browser value is not a function but ' + typeof runBrowserModule.run_browser);
30277
+ }
30278
+ cachedRunBrowserTool = runBrowserModule.run_browser;
30279
+ return cachedRunBrowserTool;
30280
+ }
30281
+ catch (error) {
30282
+ assertsError(error);
30283
+ cachedRunBrowserToolError = error;
30284
+ throw error;
30243
30285
  }
30244
30286
  }
30245
30287
  /**
30246
- * Returns true when an unknown value is one of the remote-browser outage errors.
30288
+ * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
30289
+ *
30290
+ * This uses fully lazy resolution to keep CLI startup independent from optional browser tooling.
30291
+ * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
30292
+ *
30293
+ * @private internal utility for USE BROWSER commitment
30247
30294
  */
30248
- function isRemoteBrowserUnavailableError(error) {
30249
- return error instanceof RemoteBrowserUnavailableError;
30295
+ function resolveRunBrowserToolForNode() {
30296
+ return async (args) => {
30297
+ try {
30298
+ const runBrowserTool = loadRunBrowserToolForNode();
30299
+ return await runBrowserTool(args);
30300
+ }
30301
+ catch (error) {
30302
+ assertsError(error);
30303
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
30304
+ \`run_browser\` tool is not available in this environment.
30305
+ This commitment requires the Agents Server browser runtime with Playwright CLI.
30306
+
30307
+ ${error.name}:
30308
+ ${block(error.message)}
30309
+ `));
30310
+ }
30311
+ };
30250
30312
  }
30313
+
30251
30314
  /**
30252
- * Sanitizes a remote websocket endpoint so debug payloads never expose path secrets.
30315
+ * Resolves the server-side implementation of the send_email tool for Node.js environments.
30316
+ *
30317
+ * This uses a lazy require so the core package can still load even if the Agents Server
30318
+ * module is unavailable. When the server tool cannot be resolved, a fallback implementation
30319
+ * throws a helpful error message.
30320
+ *
30321
+ * @private internal utility for USE EMAIL commitment
30253
30322
  */
30254
- function sanitizeRemoteBrowserEndpoint(wsEndpoint) {
30255
- var _a, _b;
30323
+ function resolveSendEmailToolForNode() {
30256
30324
  try {
30257
- const parsedEndpoint = new URL(wsEndpoint);
30258
- return {
30259
- protocol: parsedEndpoint.protocol || null,
30260
- host: parsedEndpoint.hostname || null,
30261
- port: parsedEndpoint.port ? Number.parseInt(parsedEndpoint.port, 10) : null,
30262
- };
30325
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
30326
+ const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
30327
+ if (typeof send_email !== 'function') {
30328
+ throw new Error('send_email value is not a function but ' + typeof send_email);
30329
+ }
30330
+ return send_email;
30263
30331
  }
30264
- catch (_c) {
30265
- const hostPortMatch = wsEndpoint.trim().match(/^(?:wss?:\/\/)?(?<host>[^:/?#]+)(?::(?<port>\d{1,5}))?/i);
30266
- const host = ((_a = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _a === void 0 ? void 0 : _a.host) || null;
30267
- const parsedPort = (_b = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _b === void 0 ? void 0 : _b.port;
30268
- return {
30269
- protocol: wsEndpoint.startsWith('wss://') ? 'wss:' : wsEndpoint.startsWith('ws://') ? 'ws:' : null,
30270
- host,
30271
- port: parsedPort ? Number.parseInt(parsedPort, 10) : null,
30332
+ catch (error) {
30333
+ const normalizedError = error instanceof Error
30334
+ ? error
30335
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
30336
+ return async () => {
30337
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
30338
+ \`send_email\` tool is not available in this environment.
30339
+ This commitment requires Agents Server runtime with wallet-backed SMTP sending.
30340
+
30341
+ ${normalizedError.name}:
30342
+ ${block(normalizedError.message)}
30343
+ `));
30272
30344
  };
30273
30345
  }
30274
30346
  }
30347
+
30275
30348
  /**
30276
- * Extracts network-like error code from unknown error payload.
30349
+ * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
30350
+ *
30351
+ * Uses lazy require so core package can load outside Agents Server.
30352
+ *
30353
+ * @private internal utility for USE SPAWN commitment
30277
30354
  */
30278
- function extractNetworkErrorCode(error) {
30279
- var _a;
30280
- if (error && typeof error === 'object') {
30281
- const maybeCode = error.code;
30282
- if (typeof maybeCode === 'string' && maybeCode.trim()) {
30283
- return maybeCode.trim().toUpperCase();
30355
+ function resolveSpawnAgentToolForNode() {
30356
+ try {
30357
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
30358
+ const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
30359
+ if (typeof spawn_agent !== 'function') {
30360
+ throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
30284
30361
  }
30362
+ return spawn_agent;
30285
30363
  }
30286
- const message = getErrorMessage(error);
30287
- const match = message.match(/\b(ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNRESET|EHOSTUNREACH|ENETUNREACH)\b/i);
30288
- return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || null;
30289
- }
30290
- /**
30291
- * Classifies whether an unknown error most likely represents remote browser infra outage.
30292
- */
30293
- function isRemoteBrowserInfrastructureError(error) {
30294
- const networkErrorCode = extractNetworkErrorCode(error);
30295
- if (networkErrorCode) {
30296
- return true;
30364
+ catch (error) {
30365
+ const normalizedError = error instanceof Error
30366
+ ? error
30367
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
30368
+ return async () => {
30369
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
30370
+ \`spawn_agent\` tool is not available in this environment.
30371
+ This commitment requires Agents Server runtime with agent persistence enabled.
30372
+
30373
+ ${normalizedError.name}:
30374
+ ${block(normalizedError.message)}
30375
+ `));
30376
+ };
30297
30377
  }
30298
- const message = getErrorMessage(error).toLowerCase();
30299
- const isWebSocketFailure = message.includes('websocket') ||
30300
- message.includes('<ws error>') ||
30301
- message.includes('ws connect error') ||
30302
- message.includes('socket hang up');
30303
- const hasHandshakeFailure = message.includes('unexpected server response') ||
30304
- message.includes('handshake') ||
30305
- message.includes('code=1006') ||
30306
- message.includes('disconnected');
30307
- return isWebSocketFailure && hasHandshakeFailure;
30308
- }
30309
- /**
30310
- * Converts unknown thrown values into safe string messages.
30311
- */
30312
- function getErrorMessage(error) {
30313
- return error instanceof Error ? error.message : String(error);
30314
- }
30315
- /**
30316
- * Converts unknown errors into stack payloads that are safe to render in debug mode.
30317
- */
30318
- function getErrorStack(error) {
30319
- return error instanceof Error && error.stack ? error.stack : null;
30320
30378
  }
30321
30379
 
30322
30380
  /**
30323
- * Matches unsupported characters in snapshot file suffixes.
30324
- */
30325
- const SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN = /[^a-z0-9-]/g;
30326
- /**
30327
- * Creates one filesystem-safe optional filename suffix for a snapshot.
30381
+ * Collects tool functions from all commitment definitions.
30382
+ *
30383
+ * @returns Map of tool function implementations.
30384
+ * @private internal helper for commitment tool registry
30328
30385
  */
30329
- function createSnapshotFileSuffix(rawSuffix) {
30330
- if (!rawSuffix) {
30331
- return '';
30386
+ function collectCommitmentToolFunctions() {
30387
+ const allToolFunctions = {};
30388
+ for (const commitmentDefinition of getAllCommitmentDefinitions()) {
30389
+ const toolFunctions = commitmentDefinition.getToolFunctions();
30390
+ for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
30391
+ if (allToolFunctions[funcName] !== undefined &&
30392
+ just(false) /* <- Note: [??] How to deal with commitment aliases */) {
30393
+ throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
30394
+ }
30395
+ allToolFunctions[funcName] = funcImpl;
30396
+ }
30332
30397
  }
30333
- const normalized = rawSuffix
30334
- .trim()
30335
- .toLowerCase()
30336
- .replace(/\s+/g, '-')
30337
- .replace(SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN, '-')
30338
- .replace(/-+/g, '-')
30339
- .replace(/^-|-$/g, '');
30340
- return normalized;
30341
- }
30342
- /**
30343
- * Resolves snapshot filename for one session and optional stage suffix.
30344
- */
30345
- function resolveSnapshotFilename(sessionId, fileSuffix) {
30346
- const safeSuffix = createSnapshotFileSuffix(fileSuffix);
30347
- return safeSuffix ? `${sessionId}-${safeSuffix}.png` : `${sessionId}.png`;
30398
+ return allToolFunctions;
30348
30399
  }
30349
30400
  /**
30350
- * Creates one user-facing description for an executed browser action.
30401
+ * Creates a proxy that resolves tool functions on demand.
30402
+ *
30403
+ * @param getFunctions - Provider of current tool functions.
30404
+ * @returns Proxy exposing tool functions as properties.
30405
+ * @private internal helper for commitment tool registry
30351
30406
  */
30352
- function formatActionSummary(action) {
30353
- switch (action.type) {
30354
- case 'navigate':
30355
- return `Navigate to ${action.url}`;
30356
- case 'click':
30357
- return `Click ${action.selector}`;
30358
- case 'type':
30359
- return `Type into ${action.selector}`;
30360
- case 'wait':
30361
- return `Wait ${action.milliseconds}ms`;
30362
- case 'scroll':
30363
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
30364
- }
30407
+ function createToolFunctionsProxy(getFunctions) {
30408
+ const resolveFunctions = () => getFunctions();
30409
+ return new Proxy({}, {
30410
+ get(_target, prop) {
30411
+ if (typeof prop !== 'string') {
30412
+ return undefined;
30413
+ }
30414
+ return resolveFunctions()[prop];
30415
+ },
30416
+ has(_target, prop) {
30417
+ if (typeof prop !== 'string') {
30418
+ return false;
30419
+ }
30420
+ return prop in resolveFunctions();
30421
+ },
30422
+ ownKeys() {
30423
+ return Object.keys(resolveFunctions());
30424
+ },
30425
+ getOwnPropertyDescriptor(_target, prop) {
30426
+ if (typeof prop !== 'string') {
30427
+ return undefined;
30428
+ }
30429
+ const value = resolveFunctions()[prop];
30430
+ if (value === undefined) {
30431
+ return undefined;
30432
+ }
30433
+ return {
30434
+ enumerable: true,
30435
+ configurable: true,
30436
+ writable: false,
30437
+ value,
30438
+ };
30439
+ },
30440
+ });
30365
30441
  }
30366
30442
  /**
30367
- * Screenshot/artifact and page-cleanup helpers for `run_browser`.
30368
- *
30369
- * @private function of `run_browser`
30443
+ * Note: [💞] Ignore a discrepancy between file name and entity name
30370
30444
  */
30371
- const runBrowserArtifacts = {
30445
+
30446
+ const nodeToolFunctions = {
30372
30447
  /**
30373
- * Captures a screenshot artifact for the current page and returns relative path.
30448
+ * @@@
30449
+ *
30450
+ * Note: [??] This function has implementation both for browser and node, this is the full one for node
30374
30451
  */
30375
- async captureSnapshot(page, sessionId, fileSuffix) {
30376
- const snapshotFilename = resolveSnapshotFilename(sessionId, fileSuffix);
30377
- const snapshotDirectoryPath = resolveRunBrowserArtifactStorageDirectory();
30378
- const snapshotPath = resolveRunBrowserArtifactFilesystemPath(snapshotFilename);
30379
- try {
30380
- await mkdir(snapshotDirectoryPath, { recursive: true });
30381
- try {
30382
- await page.screenshot({ path: snapshotPath, fullPage: true });
30383
- }
30384
- catch (error) {
30385
- console.warn('[run_browser] Full-page snapshot failed, retrying viewport-only screenshot', {
30386
- sessionId,
30387
- snapshotFilename,
30388
- error: getErrorMessage(error),
30389
- });
30390
- await page.screenshot({ path: snapshotPath, fullPage: false });
30391
- }
30392
- return resolveRunBrowserArtifactPublicPath(snapshotFilename);
30393
- }
30394
- catch (error) {
30395
- console.error('[run_browser] Failed to capture snapshot', {
30396
- sessionId,
30397
- snapshotFilename,
30398
- error: getErrorMessage(error),
30399
- });
30400
- return null;
30401
- }
30452
+ async fetch_url_content(args) {
30453
+ console.log('!!!! [Tool] fetch_url_content called', { args });
30454
+ const { url } = args;
30455
+ return await fetchUrlContent(url);
30402
30456
  },
30403
30457
  /**
30404
- * Safely retrieves page title from current browser page.
30458
+ * @@@
30459
+ *
30460
+ * Note: [??] This function has implementation both for browser and node, this is the server one for node
30405
30461
  */
30406
- async getPageTitle(page) {
30407
- try {
30408
- return await page.title();
30409
- }
30410
- catch (_a) {
30411
- return null;
30412
- }
30413
- },
30462
+ run_browser: resolveRunBrowserToolForNode(),
30414
30463
  /**
30415
- * Closes browser page and logs non-fatal cleanup errors.
30464
+ * @@@
30465
+ *
30466
+ * Note: [??] This function has implementation both for browser and node, this is the server one for node
30416
30467
  */
30417
- async cleanupPage(page, sessionId) {
30418
- if (!page) {
30419
- return;
30420
- }
30421
- try {
30422
- await page.close();
30423
- }
30424
- catch (error) {
30425
- console.error('[run_browser] Failed to cleanup browser page', {
30426
- sessionId,
30427
- error: getErrorMessage(error),
30428
- });
30429
- }
30430
- },
30468
+ send_email: resolveSendEmailToolForNode(),
30431
30469
  /**
30432
- * Captures one screenshot artifact and enriches it with page metadata.
30470
+ * @@@
30471
+ *
30472
+ * Note: [??] This function has implementation both for browser and node, this is the server one for node
30433
30473
  */
30434
- async captureSnapshotArtifact(options) {
30435
- const { page, sessionId, label, fileSuffix, actionIndex, action } = options;
30436
- const path = await this.captureSnapshot(page, sessionId, fileSuffix);
30437
- if (!path) {
30438
- return null;
30439
- }
30440
- const actionSummary = action ? formatActionSummary(action) : undefined;
30441
- return {
30442
- kind: 'screenshot',
30443
- label,
30444
- path,
30445
- capturedAt: new Date().toISOString(),
30446
- url: page.url(),
30447
- title: await this.getPageTitle(page),
30448
- actionIndex,
30449
- actionSummary,
30450
- };
30451
- },
30452
- };
30453
-
30454
- /**
30455
- * Shared constants used by the `run_browser` tool.
30456
- *
30457
- * @private internal constants of `run_browser`
30458
- */
30459
- const runBrowserConstants = {
30460
- sessionPrefix: 'agents-server-run-browser',
30461
- snapshotDirectory: '.playwright-cli',
30462
- resultSchema: 'promptbook/run-browser@1',
30463
- defaultWaitMs: 1000,
30464
- maxWaitMs: 60000,
30465
- defaultScrollPixels: 800,
30466
- defaultNavigationTimeoutMs: 20000,
30467
- defaultActionTimeoutMs: 15000,
30468
- fallbackDynamicContentWarning: 'Remote browser is unavailable. Fallback scraping was used and dynamic content may be missing.',
30469
- validationErrorCode: 'RUN_BROWSER_VALIDATION_ERROR',
30470
- navigationFailedErrorCode: 'RUN_BROWSER_NAVIGATION_FAILED',
30471
- actionFailedErrorCode: 'RUN_BROWSER_ACTION_FAILED',
30472
- cancelledErrorCode: 'RUN_BROWSER_CANCELLED',
30473
- unknownErrorCode: 'RUN_BROWSER_UNKNOWN_ERROR',
30474
+ spawn_agent: resolveSpawnAgentToolForNode(),
30475
+ // TODO: !!!! Unhardcode, make proper server function register from definitions
30474
30476
  };
30475
-
30476
- const config = ConfigChecker.from({
30477
- ...process.env,
30478
- // Note: To expose env variables to the browser, using this seemingly strange syntax:
30479
- // @see https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser
30480
- NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
30481
- NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV,
30482
- NEXT_PUBLIC_VERCEL_TARGET_ENV: process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
30483
- NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
30484
- NEXT_PUBLIC_VERCEL_BRANCH_URL: process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL,
30485
- NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL: process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL,
30486
- NEXT_PUBLIC_VERCEL_GIT_PROVIDER: process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
30487
- NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
30488
- NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
30489
- NEXT_PUBLIC_VERCEL_GIT_REPO_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_ID,
30490
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
30491
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
30492
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
30493
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME,
30494
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
30495
- NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA,
30496
- NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
30497
- });
30477
+ const nodeToolFunctionsProxy = createToolFunctionsProxy(() => ({
30478
+ ...collectCommitmentToolFunctions(),
30479
+ ...nodeToolFunctions,
30480
+ }));
30498
30481
  /**
30499
- * Public URL of the deployment, e.g. "https://my-app.vercel.app"
30482
+ * Gets all function implementations provided by all commitments
30500
30483
  *
30501
- * Note: When a request resolves through the global `_Server` registry,
30502
- * this URL will be overridden by the matched server domain.
30503
- */
30504
- config.get('NEXT_PUBLIC_SITE_URL').url().value;
30505
- /**
30506
- * [♐️] Vercel environment: "development" | "preview" | "production"
30507
- */
30508
- config.get('NEXT_PUBLIC_VERCEL_ENV').value;
30509
- /**
30510
- * [♐️] Target environment – can be system or custom
30511
- */
30512
- config.get('NEXT_PUBLIC_VERCEL_TARGET_ENV').value;
30513
- /**
30514
- * [♐️] Deployment URL (without https://), e.g. "my-app-abc123.vercel.app"
30515
- */
30516
- config.get('NEXT_PUBLIC_VERCEL_URL').value;
30517
- /**
30518
- * [♐️] Branch URL (without https://), only for branch deployments
30519
- */
30520
- config.get('NEXT_PUBLIC_VERCEL_BRANCH_URL').value;
30521
- /**
30522
- * [♐️] Production domain of the project
30523
- */
30524
- config.get('NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL').value;
30525
- /**
30526
- * [♐️] Git provider (github | gitlab | bitbucket)
30527
- */
30528
- config.get('NEXT_PUBLIC_VERCEL_GIT_PROVIDER').value;
30529
- /**
30530
- * [♐️] Repository owner (e.g. "hejny")
30531
- */
30532
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER').value;
30533
- /**
30534
- * [♐️] Repository slug (e.g. "my-project")
30535
- */
30536
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG').value;
30537
- /**
30538
- * [♐️] Repository internal ID
30539
- */
30540
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_ID').value;
30541
- /**
30542
- * [♐️] Git commit SHA (short or long)
30543
- */
30544
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA').value;
30545
- /**
30546
- * [♐️] Commit message used for this deployment
30547
- */
30548
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE').value;
30549
- /**
30550
- * [♐️] Branch name (ref), e.g. "main"
30551
- */
30552
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF').value;
30553
- /**
30554
- * Author name of the commit
30555
- */
30556
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME').value;
30557
- /**
30558
- * [♐️] Author login/username
30559
- */
30560
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN').value;
30561
- /**
30562
- * [♐️] Previous deployment commit SHA (if exists)
30563
- */
30564
- config.get('NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA').value;
30565
- /**
30566
- * [♐️] Pull Request ID for PR-based deployments
30484
+ * Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
30485
+ *
30486
+ * @public exported from `@promptbook/node`
30567
30487
  */
30568
- config.get('NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID').value;
30488
+ function getAllCommitmentsToolFunctionsForNode() {
30489
+ if (!$isRunningInNode()) {
30490
+ throw new EnvironmentMismatchError(spaceTrim(`
30491
+ Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
30492
+
30493
+ - In browser use getAllCommitmentsToolFunctionsForBrowser instead.
30494
+ - This function can include server-only tools which cannot run in browser environment.
30495
+
30496
+ `));
30497
+ }
30498
+ return nodeToolFunctionsProxy;
30499
+ }
30569
30500
  /**
30570
- * Supabase table prefix
30571
- *
30572
- * This remains the fallback/default prefix used before `_Server` contains records
30573
- * or for local development requests.
30501
+ * Note: [??] Code in this file should never be never released in packages that could be imported into browser environment
30502
+ * TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
30574
30503
  */
30575
- config.get('SUPABASE_TABLE_PREFIX').value;
30504
+
30576
30505
  /**
30577
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
30506
+ * Attempts to locate the specified application on a Linux system using the 'which' command.
30507
+ * Returns the path to the executable if found, or null otherwise.
30578
30508
  *
30579
- * When set, browser automation will connect to this remote server instead of launching a local browser.
30580
- * This is useful for environments like Vercel where running a full browser locally is not possible.
30581
- * Leave empty to use local browser mode.
30509
+ * @private within the repository
30582
30510
  */
30583
- const rawRemoteBrowserUrl = config.get('REMOTE_BROWSER_URL').value;
30511
+ async function locateAppOnLinux({ linuxWhich, }) {
30512
+ try {
30513
+ const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
30514
+ return result.trim();
30515
+ }
30516
+ catch (error) {
30517
+ assertsError(error);
30518
+ return null;
30519
+ }
30520
+ }
30584
30521
  /**
30585
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
30586
- *
30587
- * When set, browser automation will connect to this remote server instead of launching a local browser.
30588
- * This is useful for environments like Vercel where running a full browser locally is not possible.
30589
- * Leave empty to use local browser mode.
30522
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
30523
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
30590
30524
  */
30591
- const REMOTE_BROWSER_URL = typeof rawRemoteBrowserUrl === 'string' ? rawRemoteBrowserUrl : '';
30592
30525
 
30593
30526
  /**
30594
- * Reads a positive integer value from environment variables.
30527
+ * Checks if the file is executable
30528
+ *
30529
+ * @private within the repository
30595
30530
  */
30596
- function resolvePositiveIntFromEnv$1(variableName, defaultValue) {
30597
- const rawValue = process.env[variableName];
30598
- if (!rawValue || !rawValue.trim()) {
30599
- return defaultValue;
30531
+ async function isExecutable(path, fs) {
30532
+ try {
30533
+ await fs.access(path, fs.constants.X_OK);
30534
+ return true;
30600
30535
  }
30601
- const parsed = Number.parseInt(rawValue.trim(), 10);
30602
- if (!Number.isFinite(parsed) || parsed <= 0) {
30603
- return defaultValue;
30536
+ catch (error) {
30537
+ return false;
30604
30538
  }
30605
- return parsed;
30606
30539
  }
30607
30540
  /**
30608
- * Runtime helpers for mode/session/timeout handling in `run_browser`.
30609
- *
30610
- * @private function of `run_browser`
30611
- */
30612
- const runBrowserRuntime = {
30613
- /**
30614
- * Creates a dedicated session id for one tool invocation.
30615
- */
30616
- createRunBrowserSessionId() {
30617
- return `${runBrowserConstants.sessionPrefix}-${randomUUID()}`;
30618
- },
30619
- /**
30620
- * Determines whether the browser tool is running in local or remote mode.
30621
- */
30622
- resolveExecutionMode() {
30623
- return REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0 ? 'remote' : 'local';
30624
- },
30625
- /**
30626
- * Converts the execution mode into a human-readable label.
30627
- */
30628
- formatExecutionMode(mode) {
30629
- return mode === 'remote' ? 'remote-browser' : 'local-browser';
30630
- },
30631
- /**
30632
- * Resolves timeout configuration from env defaults and optional call overrides.
30633
- */
30634
- resolveTimeoutConfiguration(overrides) {
30635
- const envNavigationTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_NAVIGATION_TIMEOUT_MS', runBrowserConstants.defaultNavigationTimeoutMs);
30636
- const envActionTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_ACTION_TIMEOUT_MS', runBrowserConstants.defaultActionTimeoutMs);
30637
- const navigationTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.navigationMs) && Number.isFinite(overrides.navigationMs) && overrides.navigationMs > 0
30638
- ? Math.floor(overrides.navigationMs)
30639
- : envNavigationTimeoutMs;
30640
- const actionTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.actionMs) && Number.isFinite(overrides.actionMs) && overrides.actionMs > 0
30641
- ? Math.floor(overrides.actionMs)
30642
- : envActionTimeoutMs;
30643
- return {
30644
- navigationTimeoutMs,
30645
- actionTimeoutMs,
30646
- };
30647
- },
30648
- };
30649
-
30650
- /**
30651
- * Error classification and cancellation helpers used by `run_browser`.
30652
- *
30653
- * @private function of `run_browser`
30654
- */
30655
- const runBrowserErrorHandling = {
30656
- /**
30657
- * Creates one tagged ParseError used for deterministic input validation failures.
30658
- */
30659
- createRunBrowserValidationError(options) {
30660
- const error = new ParseError(options.message);
30661
- error.name = 'RunBrowserValidationError';
30662
- error.runBrowserCode = runBrowserConstants.validationErrorCode;
30663
- error.isRetryable = false;
30664
- error.suggestedNextSteps = [
30665
- 'Fix the action payload to match the run_browser schema.',
30666
- 'Check selectors and required action fields before retrying.',
30667
- ];
30668
- error.debug = options.debug;
30669
- return error;
30670
- },
30671
- /**
30672
- * Creates one tagged KnowledgeScrapeError used for navigation failures.
30673
- */
30674
- createRunBrowserNavigationError(options) {
30675
- const error = new KnowledgeScrapeError(options.message);
30676
- error.name = 'RunBrowserNavigationError';
30677
- error.runBrowserCode = runBrowserConstants.navigationFailedErrorCode;
30678
- error.isRetryable = false;
30679
- error.suggestedNextSteps = [
30680
- 'Verify the URL is reachable and not blocked.',
30681
- 'Retry with a simpler action sequence.',
30682
- ];
30683
- error.debug = options.debug;
30684
- error.cause = options.cause;
30685
- return error;
30686
- },
30687
- /**
30688
- * Creates one tagged KnowledgeScrapeError used for action failures.
30689
- */
30690
- createRunBrowserActionError(options) {
30691
- const error = new KnowledgeScrapeError(options.message);
30692
- error.name = 'RunBrowserActionError';
30693
- error.runBrowserCode = runBrowserConstants.actionFailedErrorCode;
30694
- error.isRetryable = false;
30695
- error.suggestedNextSteps = [
30696
- 'Verify selectors and action values.',
30697
- 'Reduce the action sequence to isolate the failing step.',
30698
- ];
30699
- error.debug = options.debug;
30700
- error.cause = options.cause;
30701
- return error;
30702
- },
30703
- /**
30704
- * Creates one tagged KnowledgeScrapeError used for cancellation.
30705
- */
30706
- createRunBrowserCancelledError(options) {
30707
- const error = new KnowledgeScrapeError(options.message);
30708
- error.name = 'RunBrowserCancelledError';
30709
- error.runBrowserCode = runBrowserConstants.cancelledErrorCode;
30710
- error.isRetryable = true;
30711
- error.suggestedNextSteps = [
30712
- 'Retry while request context is still active.',
30713
- 'Increase timeout if operation is expected to run longer.',
30714
- ];
30715
- error.debug = options.debug;
30716
- error.cause = options.cause;
30717
- return error;
30718
- },
30719
- /**
30720
- * Checks whether an unknown error carries run_browser classification tags.
30721
- */
30722
- isTaggedRunBrowserError(error) {
30723
- if (!error || typeof error !== 'object') {
30724
- return false;
30725
- }
30726
- const candidate = error;
30727
- return (typeof candidate.runBrowserCode === 'string' &&
30728
- typeof candidate.isRetryable === 'boolean' &&
30729
- Array.isArray(candidate.suggestedNextSteps) &&
30730
- typeof candidate.debug === 'object' &&
30731
- candidate.debug !== null);
30732
- },
30733
- /**
30734
- * Converts unknown errors into structured tool error payloads.
30735
- */
30736
- classifyRunBrowserToolError(options) {
30737
- if (isRemoteBrowserUnavailableError(options.error)) {
30738
- return {
30739
- code: options.error.code,
30740
- message: options.error.message,
30741
- isRetryable: options.error.isRetryable,
30742
- suggestedNextSteps: options.error.suggestedNextSteps,
30743
- debug: {
30744
- ...options.error.debug,
30745
- sessionId: options.sessionId,
30746
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
30747
- },
30748
- };
30749
- }
30750
- if (this.isTaggedRunBrowserError(options.error)) {
30751
- return {
30752
- code: options.error.runBrowserCode,
30753
- message: options.error.message,
30754
- isRetryable: options.error.isRetryable,
30755
- suggestedNextSteps: options.error.suggestedNextSteps,
30756
- debug: {
30757
- ...options.error.debug,
30758
- sessionId: options.sessionId,
30759
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
30760
- },
30761
- };
30762
- }
30763
- const remoteBrowserEndpoint = REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0
30764
- ? sanitizeRemoteBrowserEndpoint(REMOTE_BROWSER_URL.trim())
30765
- : null;
30766
- const message = getErrorMessage(options.error);
30767
- return {
30768
- code: runBrowserConstants.unknownErrorCode,
30769
- message,
30770
- isRetryable: false,
30771
- suggestedNextSteps: ['Inspect debug details to identify the failing phase.', 'Retry with fewer actions.'],
30772
- debug: {
30773
- sessionId: options.sessionId,
30774
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
30775
- remoteBrowserEndpoint,
30776
- message,
30777
- stack: getErrorStack(options.error),
30778
- },
30779
- };
30780
- },
30781
- /**
30782
- * Asserts that the run was not aborted.
30783
- */
30784
- assertNotAborted(signal, sessionId) {
30785
- if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
30786
- return;
30787
- }
30788
- throw this.createRunBrowserCancelledError({
30789
- message: 'run_browser execution was cancelled.',
30790
- debug: { sessionId },
30791
- });
30792
- },
30793
- /**
30794
- * Returns true when the tool error represents remote browser unavailability.
30795
- */
30796
- isRemoteBrowserUnavailableCode(code) {
30797
- return code === REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
30798
- },
30799
- };
30800
-
30801
- /**
30802
- * In-memory observability counters for browser tool execution.
30803
- */
30804
- const RUN_BROWSER_OBSERVABILITY = {
30805
- totalRuns: 0,
30806
- fallbackRuns: 0,
30807
- errorCodeCounts: {},
30808
- };
30809
- /**
30810
- * Observability counters and metric logging for `run_browser`.
30811
- *
30812
- * @private function of `run_browser`
30813
- */
30814
- const runBrowserObservability = {
30815
- /**
30816
- * Increments total-run counter and returns the updated value.
30817
- */
30818
- incrementTotalRuns() {
30819
- RUN_BROWSER_OBSERVABILITY.totalRuns++;
30820
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
30821
- },
30822
- /**
30823
- * Returns current total run count.
30824
- */
30825
- getTotalRuns() {
30826
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
30827
- },
30828
- /**
30829
- * Increments fallback counter and returns updated metrics.
30830
- */
30831
- incrementFallbackRunsAndGetMetrics() {
30832
- RUN_BROWSER_OBSERVABILITY.fallbackRuns++;
30833
- return {
30834
- fallbackRuns: RUN_BROWSER_OBSERVABILITY.fallbackRuns,
30835
- fallbackRate: RUN_BROWSER_OBSERVABILITY.totalRuns === 0
30836
- ? 0
30837
- : RUN_BROWSER_OBSERVABILITY.fallbackRuns / RUN_BROWSER_OBSERVABILITY.totalRuns,
30838
- };
30839
- },
30840
- /**
30841
- * Increments one error-code counter and returns the updated value.
30842
- */
30843
- incrementRunBrowserErrorCodeCounter(code) {
30844
- const currentValue = RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] || 0;
30845
- const nextValue = currentValue + 1;
30846
- RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] = nextValue;
30847
- return nextValue;
30848
- },
30849
- /**
30850
- * Writes one structured metric line for browser-tool observability.
30851
- */
30852
- logRunBrowserMetric(options) {
30853
- console.info('[run_browser][metric]', {
30854
- tool: 'run_browser',
30855
- mode: options.mode,
30856
- sessionId: options.sessionId,
30857
- event: options.event,
30858
- ...(options.payload || {}),
30859
- });
30860
- },
30861
- };
30862
-
30863
- /**
30864
- * Computes one compact preview of a fallback scrape payload.
30865
- */
30866
- function createContentPreview(content) {
30867
- const normalized = content.replace(/\s+/g, ' ').trim();
30868
- if (normalized.length <= 280) {
30869
- return normalized;
30870
- }
30871
- return `${normalized.slice(0, 277)}...`;
30872
- }
30873
- /**
30874
- * Payload and markdown formatters for `run_browser` outcomes.
30875
- *
30876
- * @private function of `run_browser`
30877
- */
30878
- const runBrowserResultFormatting = {
30879
- /**
30880
- * Produces one structured payload consumed by chat UI browser replay renderers.
30881
- */
30882
- createResultPayload(options) {
30883
- return {
30884
- schema: runBrowserConstants.resultSchema,
30885
- sessionId: options.sessionId,
30886
- mode: options.mode,
30887
- modeUsed: options.modeUsed,
30888
- initialUrl: options.initialUrl,
30889
- finalUrl: options.finalUrl,
30890
- finalTitle: options.finalTitle,
30891
- executedActions: options.executedActions,
30892
- artifacts: options.artifacts,
30893
- warning: options.warning,
30894
- error: options.error,
30895
- fallback: options.modeUsed === 'fallback' && options.fallbackContent !== null
30896
- ? {
30897
- scraper: 'fetch_url_content',
30898
- contentPreview: createContentPreview(options.fallbackContent),
30899
- }
30900
- : null,
30901
- timing: options.timing,
30902
- };
30903
- },
30904
- /**
30905
- * Produces a model-friendly markdown summary from browser execution artifacts.
30906
- */
30907
- formatSuccessResult(options) {
30908
- const { payload, snapshotPath } = options;
30909
- return spaceTrim$1((block) => {
30910
- var _a, _b, _c;
30911
- return `
30912
- # Browser run completed
30913
-
30914
- **Session:** ${payload.sessionId}
30915
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
30916
- **Mode used:** ${payload.modeUsed}
30917
- **Initial URL:** ${payload.initialUrl}
30918
- **Executed actions:** ${payload.executedActions.length}
30919
-
30920
- ## Final page
30921
-
30922
- - URL: ${payload.finalUrl || 'Unknown'}
30923
- - Title: ${payload.finalTitle || 'Unknown'}
30924
-
30925
- ## Timings
30926
-
30927
- - Connect: ${(_a = payload.timing.connectDurationMs) !== null && _a !== void 0 ? _a : 'Unknown'} ms
30928
- - Initial navigation: ${(_b = payload.timing.initialNavigationDurationMs) !== null && _b !== void 0 ? _b : 'Unknown'} ms
30929
- - Time to first byte: ${(_c = payload.timing.timeToFirstByteMs) !== null && _c !== void 0 ? _c : 'Unknown'} ms
30930
- - Total: ${payload.timing.totalDurationMs} ms
30931
-
30932
- ${payload.artifacts.length === 0
30933
- ? ''
30934
- : `
30935
- ## Visual replay
30936
-
30937
- ${payload.artifacts
30938
- .map((artifact, index) => {
30939
- const actionPart = artifact.actionSummary ? ` (${artifact.actionSummary})` : '';
30940
- return `- ${index + 1}. ${artifact.label}${actionPart}: ${artifact.path}`;
30941
- })
30942
- .join('\n')}
30943
- `}
30944
-
30945
- ${!snapshotPath
30946
- ? ''
30947
- : `
30948
- ## Final snapshot
30949
-
30950
- ${snapshotPath}
30951
- `}
30952
-
30953
- ## Playback payload
30954
-
30955
- \`\`\`json
30956
- ${JSON.stringify(payload, null, 2)}
30957
- \`\`\`
30958
-
30959
- ${block(payload.executedActions.length === 0
30960
- ? ''
30961
- : `
30962
- ## Action log
30963
-
30964
- ${payload.executedActions
30965
- .map((action, index) => `- ${index + 1}. ${JSON.stringify(action)}`)
30966
- .join('\n')}
30967
- `)}
30968
-
30969
- Note: Browser page has been automatically closed to free up resources.
30970
- `;
30971
- });
30972
- },
30973
- /**
30974
- * Produces a model-friendly markdown payload when fallback scraping is used.
30975
- */
30976
- formatFallbackResult(options) {
30977
- const { payload, fallbackContent, requestedActions } = options;
30978
- return spaceTrim$1(`
30979
- # Browser run completed with fallback
30980
-
30981
- **Session:** ${payload.sessionId}
30982
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
30983
- **Mode used:** ${payload.modeUsed}
30984
- **Initial URL:** ${payload.initialUrl}
30985
- **Requested actions:** ${requestedActions}
30986
- **Executed actions:** ${payload.executedActions.length}
30987
- **Warning:** ${payload.warning || runBrowserConstants.fallbackDynamicContentWarning}
30988
-
30989
- ## Extracted content
30990
-
30991
- ${fallbackContent}
30992
-
30993
- ## Playback payload
30994
-
30995
- \`\`\`json
30996
- ${JSON.stringify(payload, null, 2)}
30997
- \`\`\`
30998
- `);
30999
- },
31000
- /**
31001
- * Produces a model-friendly markdown error payload from browser execution failures.
31002
- */
31003
- formatErrorResult(options) {
31004
- const { payload } = options;
31005
- const toolError = payload.error;
31006
- const suggestedNextSteps = (toolError === null || toolError === void 0 ? void 0 : toolError.suggestedNextSteps) || [];
31007
- return spaceTrim$1(`
31008
- # Browser run failed
31009
-
31010
- **Session:** ${payload.sessionId}
31011
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
31012
- **Mode used:** ${payload.modeUsed}
31013
- **Initial URL:** ${payload.initialUrl}
31014
- **Error code:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.code) || runBrowserConstants.unknownErrorCode}
31015
- **Error:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.message) || 'Unknown browser tool error'}
31016
-
31017
- ${suggestedNextSteps.length === 0
31018
- ? ''
31019
- : `
31020
- ## Suggested next steps
31021
-
31022
- ${suggestedNextSteps.map((step) => `- ${step}`).join('\n')}
31023
- `}
31024
-
31025
- ## Playback payload
31026
-
31027
- \`\`\`json
31028
- ${JSON.stringify(payload, null, 2)}
31029
- \`\`\`
31030
-
31031
- The browser tool could not complete the requested actions.
31032
- `);
31033
- },
31034
- };
31035
-
31036
- /**
31037
- * Attempts to locate the specified application on a Linux system using the 'which' command.
31038
- * Returns the path to the executable if found, or null otherwise.
31039
- *
31040
- * @private within the repository
31041
- */
31042
- async function locateAppOnLinux({ linuxWhich, }) {
31043
- try {
31044
- const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
31045
- return result.trim();
31046
- }
31047
- catch (error) {
31048
- assertsError(error);
31049
- return null;
31050
- }
31051
- }
31052
- /**
31053
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
31054
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31055
- */
31056
-
31057
- /**
31058
- * Checks if the file is executable
31059
- *
31060
- * @private within the repository
31061
- */
31062
- async function isExecutable(path, fs) {
31063
- try {
31064
- await fs.access(path, fs.constants.X_OK);
31065
- return true;
31066
- }
31067
- catch (error) {
31068
- return false;
31069
- }
31070
- }
31071
- /**
31072
- * Note: Not [~🟢~] because it is not directly dependent on `fs
31073
- * TODO: [🖇] What about symlinks?
30541
+ * Note: Not [~🟢~] because it is not directly dependent on `fs
30542
+ * TODO: [🖇] What about symlinks?
31074
30543
  */
31075
30544
 
31076
30545
  // Note: Module `userhome` has no types available, so it is imported using `require`
@@ -31182,1283 +30651,6 @@ function locateApp(options) {
31182
30651
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
31183
30652
  */
31184
30653
 
31185
- /**
31186
- * @@@
31187
- *
31188
- * @private within the repository
31189
- */
31190
- function locateChrome() {
31191
- return locateApp({
31192
- appName: 'Chrome',
31193
- linuxWhich: 'google-chrome',
31194
- windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
31195
- macOsName: 'Google Chrome',
31196
- });
31197
- }
31198
-
31199
- /**
31200
- * Creates one standard abort error for cancelled retry loops.
31201
- *
31202
- * @private utility for Agents Server runtime retries
31203
- */
31204
- function createAbortError$1() {
31205
- const error = new Error('Operation was aborted.');
31206
- error.name = 'AbortError';
31207
- return error;
31208
- }
31209
- /**
31210
- * Throws when the supplied signal is already aborted.
31211
- *
31212
- * @private utility for Agents Server runtime retries
31213
- */
31214
- function assertNotAborted(signal) {
31215
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
31216
- throw createAbortError$1();
31217
- }
31218
- }
31219
- /**
31220
- * Waits for a duration while respecting cancellation.
31221
- *
31222
- * @private utility for Agents Server runtime retries
31223
- */
31224
- async function sleepWithAbort(delayMs, signal) {
31225
- if (delayMs <= 0) {
31226
- assertNotAborted(signal);
31227
- return;
31228
- }
31229
- await new Promise((resolve, reject) => {
31230
- const timeout = setTimeout(() => {
31231
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
31232
- resolve();
31233
- }, delayMs);
31234
- const onAbort = () => {
31235
- clearTimeout(timeout);
31236
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
31237
- reject(createAbortError$1());
31238
- };
31239
- signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
31240
- });
31241
- }
31242
- /**
31243
- * Resolves the retry wait duration for one failed attempt.
31244
- *
31245
- * @private utility for Agents Server runtime retries
31246
- */
31247
- function resolveBackoffDelayMs(options) {
31248
- const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
31249
- const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
31250
- const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
31251
- return Math.max(0, Math.round(boundedDelay + jitterDelay));
31252
- }
31253
- /**
31254
- * Retries one async operation with exponential backoff and jitter.
31255
- *
31256
- * @private utility for Agents Server runtime retries
31257
- */
31258
- async function retryWithBackoff(operation, options) {
31259
- var _a, _b, _c;
31260
- const startedAt = Date.now();
31261
- const totalAttempts = Math.max(1, options.retries + 1);
31262
- const random = (_a = options.random) !== null && _a !== void 0 ? _a : Math.random;
31263
- const sleep = (_b = options.sleep) !== null && _b !== void 0 ? _b : sleepWithAbort;
31264
- for (let attempt = 1; attempt <= totalAttempts; attempt++) {
31265
- assertNotAborted(options.signal);
31266
- try {
31267
- const value = await operation(attempt);
31268
- return {
31269
- value,
31270
- attempts: attempt,
31271
- durationMs: Date.now() - startedAt,
31272
- };
31273
- }
31274
- catch (error) {
31275
- const isLastAttempt = attempt >= totalAttempts;
31276
- const isRetryable = options.shouldRetry ? options.shouldRetry(error, attempt) : true;
31277
- if (isLastAttempt || !isRetryable) {
31278
- throw error;
31279
- }
31280
- const delayMs = resolveBackoffDelayMs({
31281
- attempt,
31282
- initialDelayMs: options.initialDelayMs,
31283
- maxDelayMs: options.maxDelayMs,
31284
- backoffFactor: options.backoffFactor,
31285
- jitterRatio: options.jitterRatio,
31286
- random,
31287
- });
31288
- (_c = options.onRetry) === null || _c === void 0 ? void 0 : _c.call(options, {
31289
- attempt,
31290
- retries: options.retries,
31291
- delayMs,
31292
- error,
31293
- });
31294
- await sleep(delayMs, options.signal);
31295
- }
31296
- }
31297
- throw new Error('Retry loop exited unexpectedly.');
31298
- }
31299
-
31300
- const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
31301
- /**
31302
- * Default remote browser connect timeout in milliseconds.
31303
- */
31304
- const DEFAULT_REMOTE_CONNECT_TIMEOUT_MS = 10000;
31305
- /**
31306
- * Default retry count for remote browser connection establishment.
31307
- */
31308
- const DEFAULT_REMOTE_CONNECT_RETRIES = 2;
31309
- /**
31310
- * Default initial retry delay for remote browser connection.
31311
- */
31312
- const DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS = 250;
31313
- /**
31314
- * Default maximum retry delay for remote browser connection.
31315
- */
31316
- const DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS = 1000;
31317
- /**
31318
- * Default exponential multiplier for remote browser retry delay.
31319
- */
31320
- const DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR = 4;
31321
- /**
31322
- * Default retry jitter ratio for remote browser connection.
31323
- */
31324
- const DEFAULT_REMOTE_CONNECT_JITTER_RATIO = 0.2;
31325
- /**
31326
- * In-memory metrics counters for remote browser connect attempts.
31327
- */
31328
- const REMOTE_BROWSER_CONNECT_METRICS = {
31329
- success: 0,
31330
- failure: 0,
31331
- };
31332
- /**
31333
- * Reads a positive integer from environment variables with a fallback default.
31334
- */
31335
- function resolvePositiveIntFromEnv(variableName, defaultValue) {
31336
- const rawValue = process.env[variableName];
31337
- if (!rawValue || !rawValue.trim()) {
31338
- return defaultValue;
31339
- }
31340
- const parsed = Number.parseInt(rawValue.trim(), 10);
31341
- if (!Number.isFinite(parsed) || parsed <= 0) {
31342
- return defaultValue;
31343
- }
31344
- return parsed;
31345
- }
31346
- /**
31347
- * Reads a positive number from environment variables with a fallback default.
31348
- */
31349
- function resolvePositiveNumberFromEnv(variableName, defaultValue) {
31350
- const rawValue = process.env[variableName];
31351
- if (!rawValue || !rawValue.trim()) {
31352
- return defaultValue;
31353
- }
31354
- const parsed = Number.parseFloat(rawValue.trim());
31355
- if (!Number.isFinite(parsed) || parsed <= 0) {
31356
- return defaultValue;
31357
- }
31358
- return parsed;
31359
- }
31360
- /**
31361
- * Reads a non-negative integer from environment variables with a fallback default.
31362
- */
31363
- function resolveNonNegativeIntFromEnv(variableName, defaultValue) {
31364
- const rawValue = process.env[variableName];
31365
- if (!rawValue || !rawValue.trim()) {
31366
- return defaultValue;
31367
- }
31368
- const parsed = Number.parseInt(rawValue.trim(), 10);
31369
- if (!Number.isFinite(parsed) || parsed < 0) {
31370
- return defaultValue;
31371
- }
31372
- return parsed;
31373
- }
31374
- /**
31375
- * Reads a non-negative number from environment variables with a fallback default.
31376
- */
31377
- function resolveNonNegativeNumberFromEnv(variableName, defaultValue) {
31378
- const rawValue = process.env[variableName];
31379
- if (!rawValue || !rawValue.trim()) {
31380
- return defaultValue;
31381
- }
31382
- const parsed = Number.parseFloat(rawValue.trim());
31383
- if (!Number.isFinite(parsed) || parsed < 0) {
31384
- return defaultValue;
31385
- }
31386
- return parsed;
31387
- }
31388
- /**
31389
- * Creates one standard abort error.
31390
- */
31391
- function createAbortError() {
31392
- const error = new Error('Browser connection request was aborted.');
31393
- error.name = 'AbortError';
31394
- return error;
31395
- }
31396
- /**
31397
- * Provides browser context instances with support for both local and remote browser connections.
31398
- *
31399
- * This provider manages browser lifecycle and supports:
31400
- * - Local mode: Launches a persistent Chromium context on the same machine
31401
- * - Remote mode: Connects to a remote Playwright browser via WebSocket
31402
- *
31403
- * The remote mode is useful for environments like Vercel where running a full browser
31404
- * is not possible due to resource constraints.
31405
- *
31406
- * @private internal utility for Agents Server browser tools
31407
- */
31408
- class BrowserConnectionProvider {
31409
- /**
31410
- * Creates a new BrowserConnectionProvider.
31411
- *
31412
- * @param options - Provider options
31413
- * @param options.isVerbose - Enable verbose logging
31414
- */
31415
- constructor(options = {}) {
31416
- var _a, _b, _c, _d, _e, _f, _g, _h;
31417
- this.browserContext = null;
31418
- this.connectionMode = null;
31419
- this.isVerbose = (_a = options.isVerbose) !== null && _a !== void 0 ? _a : false;
31420
- this.remoteConnectTimeoutMs =
31421
- (_b = options.remoteConnectTimeoutMs) !== null && _b !== void 0 ? _b : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_TIMEOUT_MS', DEFAULT_REMOTE_CONNECT_TIMEOUT_MS);
31422
- this.remoteConnectRetries =
31423
- (_c = options.remoteConnectRetries) !== null && _c !== void 0 ? _c : resolveNonNegativeIntFromEnv('RUN_BROWSER_CONNECT_RETRIES', DEFAULT_REMOTE_CONNECT_RETRIES);
31424
- this.remoteConnectBackoffInitialMs =
31425
- (_d = options.remoteConnectBackoffInitialMs) !== null && _d !== void 0 ? _d : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_INITIAL_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS);
31426
- this.remoteConnectBackoffMaxMs =
31427
- (_e = options.remoteConnectBackoffMaxMs) !== null && _e !== void 0 ? _e : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_MAX_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS);
31428
- this.remoteConnectBackoffFactor =
31429
- (_f = options.remoteConnectBackoffFactor) !== null && _f !== void 0 ? _f : resolvePositiveNumberFromEnv('RUN_BROWSER_CONNECT_BACKOFF_FACTOR', DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR);
31430
- this.remoteConnectJitterRatio =
31431
- (_g = options.remoteConnectJitterRatio) !== null && _g !== void 0 ? _g : resolveNonNegativeNumberFromEnv('RUN_BROWSER_CONNECT_JITTER_RATIO', DEFAULT_REMOTE_CONNECT_JITTER_RATIO);
31432
- this.random = (_h = options.random) !== null && _h !== void 0 ? _h : Math.random;
31433
- this.sleep = options.sleep;
31434
- }
31435
- /**
31436
- * Gets a browser context, creating a new one if needed.
31437
- *
31438
- * This method automatically determines whether to use local or remote browser
31439
- * based on the REMOTE_BROWSER_URL environment variable.
31440
- *
31441
- * @returns Browser context instance
31442
- */
31443
- async getBrowserContext(options = {}) {
31444
- var _a;
31445
- if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
31446
- throw createAbortError();
31447
- }
31448
- // Check if we have a cached connection that's still valid
31449
- if (this.browserContext !== null && this.isBrowserContextAlive(this.browserContext)) {
31450
- return this.browserContext;
31451
- }
31452
- // Determine connection mode from configuration
31453
- const mode = this.resolveConnectionMode();
31454
- this.connectionMode = mode;
31455
- if (this.isVerbose) {
31456
- console.info('[BrowserConnectionProvider] Creating new browser context', {
31457
- mode: mode.type,
31458
- wsEndpoint: mode.type === 'remote' ? mode.wsEndpoint : undefined,
31459
- });
31460
- }
31461
- // Create new browser context based on mode
31462
- if (mode.type === 'local') {
31463
- this.browserContext = await this.createLocalBrowserContext();
31464
- }
31465
- else {
31466
- this.browserContext = await this.createRemoteBrowserContext(mode.wsEndpoint, options);
31467
- }
31468
- return this.browserContext;
31469
- }
31470
- /**
31471
- * Closes all pages in the current browser context.
31472
- *
31473
- * This method is useful for cleanup between agent tasks without closing
31474
- * the entire browser instance.
31475
- */
31476
- async closeAllPages() {
31477
- if (!this.browserContext) {
31478
- return;
31479
- }
31480
- try {
31481
- const pages = this.browserContext.pages();
31482
- if (this.isVerbose) {
31483
- console.info('[BrowserConnectionProvider] Closing all pages', {
31484
- pageCount: pages.length,
31485
- });
31486
- }
31487
- await Promise.all(pages.map((page) => page.close().catch((error) => {
31488
- console.error('[BrowserConnectionProvider] Failed to close page', { error });
31489
- })));
31490
- }
31491
- catch (error) {
31492
- console.error('[BrowserConnectionProvider] Error closing pages', { error });
31493
- }
31494
- }
31495
- /**
31496
- * Closes the browser context and disconnects from the browser.
31497
- *
31498
- * This should be called when the browser is no longer needed to free up resources.
31499
- * For local mode, this closes the browser process. For remote mode, it disconnects
31500
- * from the remote browser but doesn't shut down the remote server.
31501
- */
31502
- async close() {
31503
- var _a;
31504
- if (!this.browserContext) {
31505
- return;
31506
- }
31507
- try {
31508
- if (this.isVerbose) {
31509
- console.info('[BrowserConnectionProvider] Closing browser context', {
31510
- mode: (_a = this.connectionMode) === null || _a === void 0 ? void 0 : _a.type,
31511
- });
31512
- }
31513
- await this.browserContext.close();
31514
- this.browserContext = null;
31515
- this.connectionMode = null;
31516
- }
31517
- catch (error) {
31518
- console.error('[BrowserConnectionProvider] Error closing browser context', { error });
31519
- // Reset state even if close fails
31520
- this.browserContext = null;
31521
- this.connectionMode = null;
31522
- }
31523
- }
31524
- /**
31525
- * Checks if a browser context is still alive and connected.
31526
- *
31527
- * @param context - Browser context to check
31528
- * @returns True if the context is connected and usable
31529
- */
31530
- isBrowserContextAlive(context) {
31531
- try {
31532
- const browser = context.browser();
31533
- return browser !== null && browser.isConnected();
31534
- }
31535
- catch (_a) {
31536
- return false;
31537
- }
31538
- }
31539
- /**
31540
- * Determines whether to use local or remote browser based on configuration.
31541
- *
31542
- * @returns Connection mode configuration
31543
- */
31544
- resolveConnectionMode() {
31545
- const remoteBrowserUrl = REMOTE_BROWSER_URL;
31546
- if (remoteBrowserUrl && remoteBrowserUrl.trim().length > 0) {
31547
- return {
31548
- type: 'remote',
31549
- wsEndpoint: remoteBrowserUrl.trim(),
31550
- };
31551
- }
31552
- return { type: 'local' };
31553
- }
31554
- /**
31555
- * Creates a local browser context using persistent Chromium.
31556
- *
31557
- * @returns Local browser context
31558
- */
31559
- async createLocalBrowserContext() {
31560
- if (this.isVerbose) {
31561
- console.info('[BrowserConnectionProvider] Launching local browser context');
31562
- }
31563
- const userDataDir = join(DEFAULT_BROWSER_USER_DATA_DIR, 'run-browser');
31564
- await mkdir(userDataDir, { recursive: true });
31565
- const launchOptions = {
31566
- headless: false,
31567
- args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
31568
- };
31569
- try {
31570
- const chromePath = await locateChrome();
31571
- launchOptions.executablePath = chromePath;
31572
- }
31573
- catch (error) {
31574
- if (this.isVerbose) {
31575
- console.warn('[BrowserConnectionProvider] Could not locate system Chrome; using Playwright bundled Chromium', {
31576
- error: error instanceof Error ? error.message : String(error),
31577
- });
31578
- }
31579
- }
31580
- return await chromium.launchPersistentContext(userDataDir, launchOptions);
31581
- }
31582
- /**
31583
- * Creates a remote browser context by connecting to a Playwright server.
31584
- *
31585
- * @param wsEndpoint - WebSocket endpoint of the remote Playwright server
31586
- * @returns Remote browser context
31587
- */
31588
- async createRemoteBrowserContext(wsEndpoint, options) {
31589
- const endpointDebug = sanitizeRemoteBrowserEndpoint(wsEndpoint);
31590
- const startedAt = Date.now();
31591
- if (this.isVerbose) {
31592
- console.info('[BrowserConnectionProvider] Connecting to remote browser', {
31593
- endpoint: endpointDebug,
31594
- connectTimeoutMs: this.remoteConnectTimeoutMs,
31595
- retries: this.remoteConnectRetries,
31596
- });
31597
- }
31598
- let attempts = 0;
31599
- try {
31600
- const connectResult = await retryWithBackoff(async (attempt) => {
31601
- attempts = attempt;
31602
- return await chromium.connect(wsEndpoint, {
31603
- timeout: this.remoteConnectTimeoutMs,
31604
- });
31605
- }, {
31606
- retries: this.remoteConnectRetries,
31607
- initialDelayMs: this.remoteConnectBackoffInitialMs,
31608
- maxDelayMs: this.remoteConnectBackoffMaxMs,
31609
- backoffFactor: this.remoteConnectBackoffFactor,
31610
- jitterRatio: this.remoteConnectJitterRatio,
31611
- signal: options.signal,
31612
- shouldRetry: (error) => isRemoteBrowserInfrastructureError(error),
31613
- onRetry: ({ attempt, delayMs, error }) => {
31614
- console.warn('[run_browser][retry]', {
31615
- tool: 'run_browser',
31616
- mode: 'remote-browser',
31617
- sessionId: options.sessionId || null,
31618
- event: 'remote_browser_connect_retry',
31619
- attempt,
31620
- delayMs,
31621
- endpoint: endpointDebug,
31622
- errorCode: extractNetworkErrorCode(error),
31623
- error: getErrorMessage(error),
31624
- });
31625
- },
31626
- random: this.random,
31627
- sleep: this.sleep,
31628
- });
31629
- const browser = connectResult.value;
31630
- // For remote connections, we need to create a new context
31631
- // Note: Remote browsers don't support persistent contexts
31632
- const context = await browser.newContext();
31633
- REMOTE_BROWSER_CONNECT_METRICS.success++;
31634
- console.info('[run_browser][metric]', {
31635
- tool: 'run_browser',
31636
- mode: 'remote-browser',
31637
- sessionId: options.sessionId || null,
31638
- event: 'remote_browser_connect_success',
31639
- attempts: connectResult.attempts,
31640
- connectDurationMs: connectResult.durationMs,
31641
- endpoint: endpointDebug,
31642
- counter: REMOTE_BROWSER_CONNECT_METRICS.success,
31643
- });
31644
- if (this.isVerbose) {
31645
- console.info('[BrowserConnectionProvider] Successfully connected to remote browser');
31646
- }
31647
- return context;
31648
- }
31649
- catch (error) {
31650
- REMOTE_BROWSER_CONNECT_METRICS.failure++;
31651
- const durationMs = Date.now() - startedAt;
31652
- const remoteInfraUnavailable = isRemoteBrowserInfrastructureError(error);
31653
- if (remoteInfraUnavailable) {
31654
- const remoteBrowserUnavailableError = new RemoteBrowserUnavailableError({
31655
- message: `Remote browser is unavailable. Could not establish a websocket connection.`,
31656
- debug: {
31657
- endpoint: endpointDebug,
31658
- attempts: Math.max(1, attempts),
31659
- connectTimeoutMs: this.remoteConnectTimeoutMs,
31660
- durationMs,
31661
- networkErrorCode: extractNetworkErrorCode(error),
31662
- originalMessage: getErrorMessage(error),
31663
- },
31664
- cause: error,
31665
- });
31666
- console.warn('[run_browser][metric]', {
31667
- tool: 'run_browser',
31668
- mode: 'remote-browser',
31669
- sessionId: options.sessionId || null,
31670
- event: 'remote_browser_connect_failure',
31671
- errorCode: remoteBrowserUnavailableError.code,
31672
- attempts: Math.max(1, attempts),
31673
- connectDurationMs: durationMs,
31674
- endpoint: endpointDebug,
31675
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
31676
- });
31677
- throw remoteBrowserUnavailableError;
31678
- }
31679
- console.error('[run_browser][metric]', {
31680
- tool: 'run_browser',
31681
- mode: 'remote-browser',
31682
- sessionId: options.sessionId || null,
31683
- event: 'remote_browser_connect_failure',
31684
- errorCode: 'REMOTE_BROWSER_CONNECT_ERROR',
31685
- attempts: Math.max(1, attempts),
31686
- connectDurationMs: durationMs,
31687
- endpoint: endpointDebug,
31688
- error: getErrorMessage(error),
31689
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
31690
- });
31691
- throw error;
31692
- }
31693
- }
31694
- }
31695
-
31696
- /**
31697
- * Singleton instance of the browser connection provider.
31698
- *
31699
- * @private internal cache for `$provideBrowserForServer`
31700
- */
31701
- let browserProvider = null;
31702
- /**
31703
- * Provides a browser context for server-side operations, with caching to reuse instances.
31704
- *
31705
- * This function supports both local and remote browser connections based on environment configuration.
31706
- * Use REMOTE_BROWSER_URL environment variable to configure a remote Playwright server.
31707
- *
31708
- * @param options - Optional runtime request options used for cancellation and logging context.
31709
- * @returns Browser context instance
31710
- */
31711
- async function $provideBrowserForServer(options = {}) {
31712
- if (browserProvider === null) {
31713
- browserProvider = new BrowserConnectionProvider({ isVerbose: false });
31714
- }
31715
- return await browserProvider.getBrowserContext(options);
31716
- }
31717
- /**
31718
- * TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
31719
- */
31720
-
31721
- /**
31722
- * Attempts to compute time-to-first-byte from Playwright response timing.
31723
- */
31724
- function resolveTimeToFirstByteMs(response) {
31725
- if (!response) {
31726
- return null;
31727
- }
31728
- try {
31729
- const timing = response.request().timing();
31730
- if (typeof (timing === null || timing === void 0 ? void 0 : timing.responseStart) === 'number' &&
31731
- typeof (timing === null || timing === void 0 ? void 0 : timing.startTime) === 'number' &&
31732
- timing.responseStart >= timing.startTime) {
31733
- return Math.round(timing.responseStart - timing.startTime);
31734
- }
31735
- }
31736
- catch (_a) {
31737
- return null;
31738
- }
31739
- return null;
31740
- }
31741
- /**
31742
- * Page open, action normalization and action execution helpers for `run_browser`.
31743
- *
31744
- * @private function of `run_browser`
31745
- */
31746
- const runBrowserWorkflow = {
31747
- /**
31748
- * Opens a new browser page and navigates to the requested URL.
31749
- */
31750
- async openPageWithUrl(options) {
31751
- runBrowserErrorHandling.assertNotAborted(options.signal, options.sessionId);
31752
- const connectStartedAt = Date.now();
31753
- const browserContext = await $provideBrowserForServer({
31754
- signal: options.signal,
31755
- sessionId: options.sessionId,
31756
- });
31757
- const connectDurationMs = Date.now() - connectStartedAt;
31758
- const page = await browserContext.newPage();
31759
- page.setDefaultNavigationTimeout(options.timeouts.navigationTimeoutMs);
31760
- page.setDefaultTimeout(options.timeouts.actionTimeoutMs);
31761
- const navigationStartedAt = Date.now();
31762
- try {
31763
- const navigationResponse = await page.goto(options.url, {
31764
- waitUntil: 'domcontentloaded',
31765
- timeout: options.timeouts.navigationTimeoutMs,
31766
- });
31767
- return {
31768
- page,
31769
- connectDurationMs,
31770
- initialNavigationDurationMs: Date.now() - navigationStartedAt,
31771
- timeToFirstByteMs: resolveTimeToFirstByteMs(navigationResponse),
31772
- };
31773
- }
31774
- catch (error) {
31775
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
31776
- message: `Failed to navigate to \`${options.url}\`.`,
31777
- debug: {
31778
- phase: 'initial-navigation',
31779
- url: options.url,
31780
- navigationTimeoutMs: options.timeouts.navigationTimeoutMs,
31781
- },
31782
- cause: error,
31783
- });
31784
- }
31785
- },
31786
- /**
31787
- * Validates and normalizes browser actions received from the model.
31788
- */
31789
- normalizeActions(actions) {
31790
- if (!actions || actions.length === 0) {
31791
- return [];
31792
- }
31793
- return actions.map((action, index) => this.normalizeAction(action, index));
31794
- },
31795
- /**
31796
- * Validates and normalizes a single action.
31797
- */
31798
- normalizeAction(action, index) {
31799
- var _a, _b, _c;
31800
- switch (action.type) {
31801
- case 'navigate': {
31802
- const url = String(action.value || '').trim();
31803
- if (!url) {
31804
- throw runBrowserErrorHandling.createRunBrowserValidationError({
31805
- message: spaceTrim$1(`Action ${index + 1}: \`navigate\` requires non-empty \`value\` URL.`),
31806
- debug: {
31807
- actionIndex: index + 1,
31808
- actionType: action.type,
31809
- },
31810
- });
31811
- }
31812
- return { type: 'navigate', url };
31813
- }
31814
- case 'click': {
31815
- const selector = String(action.selector || '').trim();
31816
- if (!selector) {
31817
- throw runBrowserErrorHandling.createRunBrowserValidationError({
31818
- message: spaceTrim$1(`Action ${index + 1}: \`click\` requires non-empty \`selector\`.`),
31819
- debug: {
31820
- actionIndex: index + 1,
31821
- actionType: action.type,
31822
- },
31823
- });
31824
- }
31825
- return { type: 'click', selector };
31826
- }
31827
- case 'type': {
31828
- const selector = String(action.selector || '').trim();
31829
- if (!selector) {
31830
- throw runBrowserErrorHandling.createRunBrowserValidationError({
31831
- message: spaceTrim$1(`Action ${index + 1}: \`type\` requires non-empty \`selector\`.`),
31832
- debug: {
31833
- actionIndex: index + 1,
31834
- actionType: action.type,
31835
- },
31836
- });
31837
- }
31838
- const text = String((_a = action.value) !== null && _a !== void 0 ? _a : '');
31839
- return { type: 'type', selector, text };
31840
- }
31841
- case 'wait': {
31842
- const requestedValue = Number.parseInt(String((_b = action.value) !== null && _b !== void 0 ? _b : runBrowserConstants.defaultWaitMs), 10);
31843
- const milliseconds = Number.isFinite(requestedValue)
31844
- ? Math.min(Math.max(requestedValue, 1), runBrowserConstants.maxWaitMs)
31845
- : runBrowserConstants.defaultWaitMs;
31846
- return { type: 'wait', milliseconds };
31847
- }
31848
- case 'scroll': {
31849
- const requestedValue = Number.parseInt(String((_c = action.value) !== null && _c !== void 0 ? _c : runBrowserConstants.defaultScrollPixels), 10);
31850
- const pixels = Number.isFinite(requestedValue) ? requestedValue : runBrowserConstants.defaultScrollPixels;
31851
- const rawSelector = String(action.selector || '').trim();
31852
- return { type: 'scroll', selector: rawSelector || null, pixels };
31853
- }
31854
- }
31855
- },
31856
- /**
31857
- * Executes one normalized browser action on a Playwright page.
31858
- */
31859
- async executeAction(options) {
31860
- const { page, action, actionIndex, timeouts, signal } = options;
31861
- runBrowserErrorHandling.assertNotAborted(signal, `action-${actionIndex}`);
31862
- try {
31863
- switch (action.type) {
31864
- case 'navigate':
31865
- await page.goto(action.url, {
31866
- waitUntil: 'domcontentloaded',
31867
- timeout: timeouts.navigationTimeoutMs,
31868
- });
31869
- return;
31870
- case 'click':
31871
- await page.locator(action.selector).first().click({ timeout: timeouts.actionTimeoutMs });
31872
- return;
31873
- case 'type':
31874
- await page.locator(action.selector).first().fill(action.text, { timeout: timeouts.actionTimeoutMs });
31875
- return;
31876
- case 'wait':
31877
- if (action.milliseconds > timeouts.actionTimeoutMs) {
31878
- throw runBrowserErrorHandling.createRunBrowserActionError({
31879
- message: `Action ${actionIndex}: \`wait\` exceeds action timeout (${timeouts.actionTimeoutMs}ms).`,
31880
- debug: {
31881
- actionIndex,
31882
- action,
31883
- actionTimeoutMs: timeouts.actionTimeoutMs,
31884
- },
31885
- });
31886
- }
31887
- await page.waitForTimeout(action.milliseconds);
31888
- return;
31889
- case 'scroll':
31890
- if (action.selector) {
31891
- await page
31892
- .locator(action.selector)
31893
- .first()
31894
- .scrollIntoViewIfNeeded({ timeout: timeouts.actionTimeoutMs });
31895
- }
31896
- await page.mouse.wheel(0, action.pixels);
31897
- return;
31898
- }
31899
- }
31900
- catch (error) {
31901
- if (runBrowserErrorHandling.isTaggedRunBrowserError(error)) {
31902
- throw error;
31903
- }
31904
- if (action.type === 'navigate') {
31905
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
31906
- message: `Action ${actionIndex}: failed to navigate to \`${action.url}\`.`,
31907
- debug: {
31908
- actionIndex,
31909
- action,
31910
- navigationTimeoutMs: timeouts.navigationTimeoutMs,
31911
- },
31912
- cause: error,
31913
- });
31914
- }
31915
- throw runBrowserErrorHandling.createRunBrowserActionError({
31916
- message: `Action ${actionIndex}: failed to execute \`${action.type}\`.`,
31917
- debug: {
31918
- actionIndex,
31919
- action,
31920
- actionTimeoutMs: timeouts.actionTimeoutMs,
31921
- },
31922
- cause: error,
31923
- });
31924
- }
31925
- },
31926
- };
31927
-
31928
- /**
31929
- * Summarizes one normalized browser action in user-facing language.
31930
- */
31931
- function formatRunBrowserActionSummary(action) {
31932
- switch (action.type) {
31933
- case 'navigate':
31934
- return `Navigate to ${action.url}`;
31935
- case 'click':
31936
- return `Click ${action.selector}`;
31937
- case 'type':
31938
- return `Type into ${action.selector}`;
31939
- case 'wait':
31940
- return `Wait ${action.milliseconds}ms`;
31941
- case 'scroll':
31942
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
31943
- }
31944
- }
31945
- /**
31946
- * Emits one incremental browser-tool update when a hidden chat-progress listener is attached.
31947
- */
31948
- function emitRunBrowserProgress(args, update) {
31949
- emitToolCallProgressFromToolArgs(args, update);
31950
- }
31951
- /**
31952
- * Returns the current timestamp in the branded ISO-8601 format used by tool-call logs.
31953
- */
31954
- function createRunBrowserLogTimestamp() {
31955
- return new Date().toISOString();
31956
- }
31957
- /**
31958
- * Executes non-graphical fallback scraping.
31959
- */
31960
- async function runFallbackScrape(url) {
31961
- return await fetchUrlContent(url);
31962
- }
31963
- /**
31964
- * Runs interactive browser automation through Playwright.
31965
- *
31966
- * @param args Tool arguments provided by the model.
31967
- * @param internalOptions Optional runtime options for cancellation.
31968
- * @returns Markdown summary with structured playback payload.
31969
- */
31970
- async function run_browser(args, internalOptions = {}) {
31971
- runBrowserObservability.incrementTotalRuns();
31972
- const startedAt = Date.now();
31973
- const sessionId = runBrowserRuntime.createRunBrowserSessionId();
31974
- const initialUrl = String(args.url || '').trim();
31975
- const mode = runBrowserRuntime.resolveExecutionMode();
31976
- const timeoutConfiguration = runBrowserRuntime.resolveTimeoutConfiguration(args.timeouts);
31977
- let page = null;
31978
- let connectDurationMs = null;
31979
- let initialNavigationDurationMs = null;
31980
- let timeToFirstByteMs = null;
31981
- try {
31982
- if (!initialUrl) {
31983
- throw runBrowserErrorHandling.createRunBrowserValidationError({
31984
- message: 'Missing required `url` argument.',
31985
- debug: {
31986
- field: 'url',
31987
- },
31988
- });
31989
- }
31990
- const normalizedActions = runBrowserWorkflow.normalizeActions(args.actions);
31991
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
31992
- const openedPage = await runBrowserWorkflow.openPageWithUrl({
31993
- url: initialUrl,
31994
- sessionId,
31995
- timeouts: timeoutConfiguration,
31996
- signal: internalOptions.signal,
31997
- });
31998
- page = openedPage.page;
31999
- connectDurationMs = openedPage.connectDurationMs;
32000
- initialNavigationDurationMs = openedPage.initialNavigationDurationMs;
32001
- timeToFirstByteMs = openedPage.timeToFirstByteMs;
32002
- emitRunBrowserProgress(args, {
32003
- state: 'PARTIAL',
32004
- log: {
32005
- createdAt: createRunBrowserLogTimestamp(),
32006
- kind: 'browser-session',
32007
- title: 'Browser ready',
32008
- message: 'Opened the initial page and started the browser session.',
32009
- payload: {
32010
- sessionId,
32011
- initialUrl,
32012
- connectDurationMs,
32013
- initialNavigationDurationMs,
32014
- timeToFirstByteMs,
32015
- },
32016
- },
32017
- });
32018
- const artifacts = [];
32019
- const initialArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
32020
- page,
32021
- sessionId,
32022
- label: 'Initial page',
32023
- fileSuffix: 'initial',
32024
- });
32025
- if (initialArtifact) {
32026
- artifacts.push(initialArtifact);
32027
- }
32028
- for (const [index, action] of normalizedActions.entries()) {
32029
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
32030
- emitRunBrowserProgress(args, {
32031
- state: 'PARTIAL',
32032
- log: {
32033
- createdAt: createRunBrowserLogTimestamp(),
32034
- kind: 'browser-action',
32035
- title: `Action ${index + 1} running`,
32036
- message: formatRunBrowserActionSummary(action),
32037
- payload: {
32038
- actionIndex: index + 1,
32039
- action,
32040
- phase: 'running',
32041
- },
32042
- },
32043
- });
32044
- await runBrowserWorkflow.executeAction({
32045
- page,
32046
- action,
32047
- actionIndex: index + 1,
32048
- timeouts: timeoutConfiguration,
32049
- signal: internalOptions.signal,
32050
- });
32051
- emitRunBrowserProgress(args, {
32052
- state: 'PARTIAL',
32053
- log: {
32054
- createdAt: createRunBrowserLogTimestamp(),
32055
- kind: 'browser-action',
32056
- title: `Action ${index + 1} finished`,
32057
- message: formatRunBrowserActionSummary(action),
32058
- payload: {
32059
- actionIndex: index + 1,
32060
- action,
32061
- phase: 'complete',
32062
- },
32063
- },
32064
- });
32065
- const actionArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
32066
- page,
32067
- sessionId,
32068
- label: `After action ${index + 1}`,
32069
- fileSuffix: `action-${String(index + 1).padStart(3, '0')}-${action.type}`,
32070
- actionIndex: index + 1,
32071
- action,
32072
- });
32073
- if (actionArtifact) {
32074
- artifacts.push(actionArtifact);
32075
- }
32076
- }
32077
- const snapshotPath = await runBrowserArtifacts.captureSnapshot(page, sessionId);
32078
- const finalUrl = page.url();
32079
- const finalTitle = await runBrowserArtifacts.getPageTitle(page);
32080
- if (snapshotPath) {
32081
- artifacts.push({
32082
- kind: 'screenshot',
32083
- label: 'Final page',
32084
- path: snapshotPath,
32085
- capturedAt: new Date().toISOString(),
32086
- url: finalUrl,
32087
- title: finalTitle,
32088
- });
32089
- }
32090
- const payload = runBrowserResultFormatting.createResultPayload({
32091
- sessionId,
32092
- mode,
32093
- modeUsed: 'remote-browser',
32094
- initialUrl,
32095
- finalUrl,
32096
- finalTitle,
32097
- executedActions: normalizedActions,
32098
- artifacts,
32099
- warning: null,
32100
- error: null,
32101
- fallbackContent: null,
32102
- timing: {
32103
- connectDurationMs,
32104
- initialNavigationDurationMs,
32105
- timeToFirstByteMs,
32106
- totalDurationMs: Date.now() - startedAt,
32107
- },
32108
- });
32109
- runBrowserObservability.logRunBrowserMetric({
32110
- event: 'run_browser_success',
32111
- sessionId,
32112
- mode: 'remote-browser',
32113
- payload: {
32114
- actions: normalizedActions.length,
32115
- connectDurationMs,
32116
- initialNavigationDurationMs,
32117
- timeToFirstByteMs,
32118
- },
32119
- });
32120
- return runBrowserResultFormatting.formatSuccessResult({
32121
- payload,
32122
- snapshotPath,
32123
- });
32124
- }
32125
- catch (error) {
32126
- const toolError = runBrowserErrorHandling.classifyRunBrowserToolError({
32127
- error,
32128
- sessionId,
32129
- mode,
32130
- });
32131
- const errorCodeCount = runBrowserObservability.incrementRunBrowserErrorCodeCounter(toolError.code);
32132
- if (runBrowserErrorHandling.isRemoteBrowserUnavailableCode(toolError.code) && initialUrl) {
32133
- const fallbackContent = await runFallbackScrape(initialUrl);
32134
- const { fallbackRuns, fallbackRate } = runBrowserObservability.incrementFallbackRunsAndGetMetrics();
32135
- emitRunBrowserProgress(args, {
32136
- state: 'PARTIAL',
32137
- log: {
32138
- createdAt: createRunBrowserLogTimestamp(),
32139
- kind: 'warning',
32140
- level: 'warning',
32141
- title: 'Fallback enabled',
32142
- message: 'Remote browser was unavailable, so fallback scraping was used instead.',
32143
- payload: {
32144
- errorCode: toolError.code,
32145
- initialUrl,
32146
- },
32147
- },
32148
- });
32149
- const payload = runBrowserResultFormatting.createResultPayload({
32150
- sessionId,
32151
- mode,
32152
- modeUsed: 'fallback',
32153
- initialUrl,
32154
- finalUrl: null,
32155
- finalTitle: null,
32156
- executedActions: [],
32157
- artifacts: [],
32158
- warning: runBrowserConstants.fallbackDynamicContentWarning,
32159
- error: toolError,
32160
- fallbackContent,
32161
- timing: {
32162
- connectDurationMs,
32163
- initialNavigationDurationMs,
32164
- timeToFirstByteMs,
32165
- totalDurationMs: Date.now() - startedAt,
32166
- },
32167
- });
32168
- runBrowserObservability.logRunBrowserMetric({
32169
- event: 'run_browser_fallback_used',
32170
- sessionId,
32171
- mode: 'fallback',
32172
- payload: {
32173
- errorCode: toolError.code,
32174
- errorCodeCount,
32175
- fallbackRuns,
32176
- totalRuns: runBrowserObservability.getTotalRuns(),
32177
- fallbackRate,
32178
- },
32179
- });
32180
- return runBrowserResultFormatting.formatFallbackResult({
32181
- payload,
32182
- fallbackContent,
32183
- requestedActions: Array.isArray(args.actions) ? args.actions.length : 0,
32184
- });
32185
- }
32186
- emitRunBrowserProgress(args, {
32187
- state: 'ERROR',
32188
- log: {
32189
- createdAt: createRunBrowserLogTimestamp(),
32190
- kind: 'error',
32191
- level: 'error',
32192
- title: 'Browser run failed',
32193
- message: toolError.message,
32194
- payload: {
32195
- code: toolError.code,
32196
- debug: toolError.debug,
32197
- },
32198
- },
32199
- });
32200
- const payload = runBrowserResultFormatting.createResultPayload({
32201
- sessionId,
32202
- mode,
32203
- modeUsed: 'remote-browser',
32204
- initialUrl,
32205
- finalUrl: page ? page.url() : null,
32206
- finalTitle: page ? await runBrowserArtifacts.getPageTitle(page) : null,
32207
- executedActions: [],
32208
- artifacts: [],
32209
- warning: null,
32210
- error: toolError,
32211
- fallbackContent: null,
32212
- timing: {
32213
- connectDurationMs,
32214
- initialNavigationDurationMs,
32215
- timeToFirstByteMs,
32216
- totalDurationMs: Date.now() - startedAt,
32217
- },
32218
- });
32219
- runBrowserObservability.logRunBrowserMetric({
32220
- event: 'run_browser_failed',
32221
- sessionId,
32222
- mode: 'remote-browser',
32223
- payload: {
32224
- errorCode: toolError.code,
32225
- errorCodeCount,
32226
- connectDurationMs,
32227
- initialNavigationDurationMs,
32228
- timeToFirstByteMs,
32229
- },
32230
- });
32231
- return runBrowserResultFormatting.formatErrorResult({
32232
- payload,
32233
- });
32234
- }
32235
- finally {
32236
- await runBrowserArtifacts.cleanupPage(page, sessionId);
32237
- }
32238
- }
32239
-
32240
- /**
32241
- * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
32242
- *
32243
- * This uses lazy `require` to keep the core package decoupled from Agents Server internals.
32244
- * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
32245
- *
32246
- * @private internal utility for USE BROWSER commitment
32247
- */
32248
- function resolveRunBrowserToolForNode() {
32249
- try {
32250
- // eslint-disable-next-line @typescript-eslint/no-var-requires
32251
- // const { run_browser } = require('../../../apps/agents-server/src/tools/run_browser');
32252
- if (typeof run_browser !== 'function') {
32253
- throw new Error('run_browser value is not a function but ' + typeof run_browser);
32254
- }
32255
- return run_browser;
32256
- }
32257
- catch (error) {
32258
- assertsError(error);
32259
- return async () => {
32260
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
32261
- \`run_browser\` tool is not available in this environment.
32262
- This commitment requires the Agents Server browser runtime with Playwright CLI.
32263
-
32264
- ${error.name}:
32265
- ${block(error.message)}
32266
- `));
32267
- };
32268
- }
32269
- }
32270
-
32271
- /**
32272
- * Resolves the server-side implementation of the send_email tool for Node.js environments.
32273
- *
32274
- * This uses a lazy require so the core package can still load even if the Agents Server
32275
- * module is unavailable. When the server tool cannot be resolved, a fallback implementation
32276
- * throws a helpful error message.
32277
- *
32278
- * @private internal utility for USE EMAIL commitment
32279
- */
32280
- function resolveSendEmailToolForNode() {
32281
- try {
32282
- // eslint-disable-next-line @typescript-eslint/no-var-requires
32283
- const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
32284
- if (typeof send_email !== 'function') {
32285
- throw new Error('send_email value is not a function but ' + typeof send_email);
32286
- }
32287
- return send_email;
32288
- }
32289
- catch (error) {
32290
- const normalizedError = error instanceof Error
32291
- ? error
32292
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
32293
- return async () => {
32294
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
32295
- \`send_email\` tool is not available in this environment.
32296
- This commitment requires Agents Server runtime with wallet-backed SMTP sending.
32297
-
32298
- ${normalizedError.name}:
32299
- ${block(normalizedError.message)}
32300
- `));
32301
- };
32302
- }
32303
- }
32304
-
32305
- /**
32306
- * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
32307
- *
32308
- * Uses lazy require so core package can load outside Agents Server.
32309
- *
32310
- * @private internal utility for USE SPAWN commitment
32311
- */
32312
- function resolveSpawnAgentToolForNode() {
32313
- try {
32314
- // eslint-disable-next-line @typescript-eslint/no-var-requires
32315
- const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
32316
- if (typeof spawn_agent !== 'function') {
32317
- throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
32318
- }
32319
- return spawn_agent;
32320
- }
32321
- catch (error) {
32322
- const normalizedError = error instanceof Error
32323
- ? error
32324
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
32325
- return async () => {
32326
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
32327
- \`spawn_agent\` tool is not available in this environment.
32328
- This commitment requires Agents Server runtime with agent persistence enabled.
32329
-
32330
- ${normalizedError.name}:
32331
- ${block(normalizedError.message)}
32332
- `));
32333
- };
32334
- }
32335
- }
32336
-
32337
- /**
32338
- * Collects tool functions from all commitment definitions.
32339
- *
32340
- * @returns Map of tool function implementations.
32341
- * @private internal helper for commitment tool registry
32342
- */
32343
- function collectCommitmentToolFunctions() {
32344
- const allToolFunctions = {};
32345
- for (const commitmentDefinition of getAllCommitmentDefinitions()) {
32346
- const toolFunctions = commitmentDefinition.getToolFunctions();
32347
- for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
32348
- if (allToolFunctions[funcName] !== undefined &&
32349
- just(false) /* <- Note: [??] How to deal with commitment aliases */) {
32350
- throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
32351
- }
32352
- allToolFunctions[funcName] = funcImpl;
32353
- }
32354
- }
32355
- return allToolFunctions;
32356
- }
32357
- /**
32358
- * Creates a proxy that resolves tool functions on demand.
32359
- *
32360
- * @param getFunctions - Provider of current tool functions.
32361
- * @returns Proxy exposing tool functions as properties.
32362
- * @private internal helper for commitment tool registry
32363
- */
32364
- function createToolFunctionsProxy(getFunctions) {
32365
- const resolveFunctions = () => getFunctions();
32366
- return new Proxy({}, {
32367
- get(_target, prop) {
32368
- if (typeof prop !== 'string') {
32369
- return undefined;
32370
- }
32371
- return resolveFunctions()[prop];
32372
- },
32373
- has(_target, prop) {
32374
- if (typeof prop !== 'string') {
32375
- return false;
32376
- }
32377
- return prop in resolveFunctions();
32378
- },
32379
- ownKeys() {
32380
- return Object.keys(resolveFunctions());
32381
- },
32382
- getOwnPropertyDescriptor(_target, prop) {
32383
- if (typeof prop !== 'string') {
32384
- return undefined;
32385
- }
32386
- const value = resolveFunctions()[prop];
32387
- if (value === undefined) {
32388
- return undefined;
32389
- }
32390
- return {
32391
- enumerable: true,
32392
- configurable: true,
32393
- writable: false,
32394
- value,
32395
- };
32396
- },
32397
- });
32398
- }
32399
- /**
32400
- * Note: [💞] Ignore a discrepancy between file name and entity name
32401
- */
32402
-
32403
- const nodeToolFunctions = {
32404
- /**
32405
- * @@@
32406
- *
32407
- * Note: [??] This function has implementation both for browser and node, this is the full one for node
32408
- */
32409
- async fetch_url_content(args) {
32410
- console.log('!!!! [Tool] fetch_url_content called', { args });
32411
- const { url } = args;
32412
- return await fetchUrlContent(url);
32413
- },
32414
- /**
32415
- * @@@
32416
- *
32417
- * Note: [??] This function has implementation both for browser and node, this is the server one for node
32418
- */
32419
- run_browser: resolveRunBrowserToolForNode(),
32420
- /**
32421
- * @@@
32422
- *
32423
- * Note: [??] This function has implementation both for browser and node, this is the server one for node
32424
- */
32425
- send_email: resolveSendEmailToolForNode(),
32426
- /**
32427
- * @@@
32428
- *
32429
- * Note: [??] This function has implementation both for browser and node, this is the server one for node
32430
- */
32431
- spawn_agent: resolveSpawnAgentToolForNode(),
32432
- // TODO: !!!! Unhardcode, make proper server function register from definitions
32433
- };
32434
- const nodeToolFunctionsProxy = createToolFunctionsProxy(() => ({
32435
- ...collectCommitmentToolFunctions(),
32436
- ...nodeToolFunctions,
32437
- }));
32438
- /**
32439
- * Gets all function implementations provided by all commitments
32440
- *
32441
- * Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
32442
- *
32443
- * @public exported from `@promptbook/node`
32444
- */
32445
- function getAllCommitmentsToolFunctionsForNode() {
32446
- if (!$isRunningInNode()) {
32447
- throw new EnvironmentMismatchError(spaceTrim(`
32448
- Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
32449
-
32450
- - In browser use getAllCommitmentsToolFunctionsForBrowser instead.
32451
- - This function can include server-only tools which cannot run in browser environment.
32452
-
32453
- `));
32454
- }
32455
- return nodeToolFunctionsProxy;
32456
- }
32457
- /**
32458
- * Note: [??] Code in this file should never be never released in packages that could be imported into browser environment
32459
- * TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
32460
- */
32461
-
32462
30654
  /**
32463
30655
  * Locates the LibreOffice executable on the current system by searching platform-specific paths.
32464
30656
  * Returns the path to the executable if found, or null otherwise.
@@ -32615,13 +30807,13 @@ function $registeredLlmToolsMessage() {
32615
30807
  });
32616
30808
  const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
32617
30809
  if (metadata.length === 0) {
32618
- return spaceTrim$2((block) => `
30810
+ return spaceTrim$1((block) => `
32619
30811
  No LLM providers are available.
32620
30812
 
32621
30813
  ${block(usedEnvMessage)}
32622
30814
  `);
32623
30815
  }
32624
- return spaceTrim$2((block) => `
30816
+ return spaceTrim$1((block) => `
32625
30817
 
32626
30818
  ${block(usedEnvMessage)}
32627
30819
 
@@ -32667,7 +30859,7 @@ function $registeredLlmToolsMessage() {
32667
30859
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
32668
30860
  }
32669
30861
  }
32670
- let providerMessage = spaceTrim$2(`
30862
+ let providerMessage = spaceTrim$1(`
32671
30863
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
32672
30864
  ${morePieces.join('; ')}
32673
30865
  `);
@@ -32801,7 +30993,7 @@ class $EnvStorage {
32801
30993
  .filter((line) => !line.startsWith(`# ${GENERATOR_WARNING_IN_ENV}`)) // Remove GENERATOR_WARNING_IN_ENV
32802
30994
  .filter((line) => !line.startsWith(`${transformedKey}=`)) // Remove existing key if present
32803
30995
  .join('\n');
32804
- const newEnvContent = spaceTrim$2((block) => `
30996
+ const newEnvContent = spaceTrim$1((block) => `
32805
30997
  ${block(updatedEnvContent)}
32806
30998
 
32807
30999
  # ${GENERATOR_WARNING_IN_ENV}
@@ -32830,7 +31022,7 @@ class $EnvStorage {
32830
31022
  */
32831
31023
  function stringifyPipelineJson(pipeline) {
32832
31024
  if (!isSerializableAsJson(pipeline)) {
32833
- throw new UnexpectedError(spaceTrim$2(`
31025
+ throw new UnexpectedError(spaceTrim$1(`
32834
31026
  Cannot stringify the pipeline, because it is not serializable as JSON
32835
31027
 
32836
31028
  There can be multiple reasons:
@@ -33013,7 +31205,7 @@ function cacheLlmTools(llmTools, options = {}) {
33013
31205
  let normalizedContent = content;
33014
31206
  normalizedContent = normalizedContent.replace(/\s+/g, ' ');
33015
31207
  normalizedContent = normalizedContent.split('\r\n').join('\n');
33016
- normalizedContent = spaceTrim$2(normalizedContent);
31208
+ normalizedContent = spaceTrim$1(normalizedContent);
33017
31209
  // Note: Do not need to save everything in the cache, just the relevant parameters
33018
31210
  const relevantParameterNames = extractParameterNames(content);
33019
31211
  const relevantParameters = Object.fromEntries(Object.entries(parameters).filter(([key]) => relevantParameterNames.has(key)));
@@ -33249,7 +31441,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
33249
31441
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
33250
31442
  if (registeredItem === undefined) {
33251
31443
  // console.log('$llmToolsRegister.list()', $llmToolsRegister.list());
33252
- throw new Error(spaceTrim$2((block) => `
31444
+ throw new Error(spaceTrim$1((block) => `
33253
31445
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
33254
31446
  Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
33255
31447
 
@@ -33317,14 +31509,14 @@ async function $provideLlmToolsFromEnv(options = {}) {
33317
31509
  const configuration = await $provideLlmToolsConfigurationFromEnv();
33318
31510
  if (configuration.length === 0) {
33319
31511
  if ($llmToolsMetadataRegister.list().length === 0) {
33320
- throw new UnexpectedError(spaceTrim$2((block) => `
31512
+ throw new UnexpectedError(spaceTrim$1((block) => `
33321
31513
  No LLM tools registered, this is probably a bug in the Promptbook library
33322
31514
 
33323
31515
  ${block($registeredLlmToolsMessage())}}
33324
31516
  `));
33325
31517
  }
33326
31518
  // TODO: [🥃]
33327
- throw new Error(spaceTrim$2((block) => `
31519
+ throw new Error(spaceTrim$1((block) => `
33328
31520
  No LLM tools found in the environment
33329
31521
 
33330
31522
  ${block($registeredLlmToolsMessage())}}
@@ -33463,7 +31655,7 @@ async function $provideScrapersForNode(tools, options) {
33463
31655
  function extractOneBlockFromMarkdown(markdown) {
33464
31656
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
33465
31657
  if (codeBlocks.length !== 1) {
33466
- throw new ParseError(spaceTrim$2((block) => `
31658
+ throw new ParseError(spaceTrim$1((block) => `
33467
31659
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
33468
31660
 
33469
31661
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -33572,7 +31764,7 @@ class JavascriptEvalExecutionTools {
33572
31764
  }
33573
31765
  // Note: [💎]
33574
31766
  // Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
33575
- const spaceTrim = (_) => spaceTrim$2(_);
31767
+ const spaceTrim = (_) => _spaceTrim(_);
33576
31768
  $preserve(spaceTrim);
33577
31769
  const removeQuotes$1 = removeQuotes;
33578
31770
  $preserve(removeQuotes$1);
@@ -33663,7 +31855,7 @@ class JavascriptEvalExecutionTools {
33663
31855
  .join('\n');
33664
31856
  // script = templateParameters(script, parameters);
33665
31857
  // <- TODO: [🧠][🥳] Should be this is one of two variants how to use parameters in script
33666
- const statementToEvaluate = spaceTrim$2((block) => `
31858
+ const statementToEvaluate = _spaceTrim((block) => `
33667
31859
 
33668
31860
  // Build-in functions:
33669
31861
  ${block(buildinFunctionsStatement)}
@@ -33678,7 +31870,7 @@ class JavascriptEvalExecutionTools {
33678
31870
  (async ()=>{ ${script} })()
33679
31871
  `);
33680
31872
  if (this.options.isVerbose) {
33681
- console.info(spaceTrim$2((block) => `
31873
+ console.info(_spaceTrim((block) => `
33682
31874
  🚀 Evaluating ${scriptLanguage} script:
33683
31875
 
33684
31876
  ${block(statementToEvaluate)}`));
@@ -33687,7 +31879,7 @@ class JavascriptEvalExecutionTools {
33687
31879
  try {
33688
31880
  result = await eval(statementToEvaluate);
33689
31881
  if (this.options.isVerbose) {
33690
- console.info(spaceTrim$2((block) => `
31882
+ console.info(_spaceTrim((block) => `
33691
31883
  🚀 Script evaluated successfully, result:
33692
31884
  ${block(valueToString(result))}
33693
31885
  `));
@@ -33706,7 +31898,7 @@ class JavascriptEvalExecutionTools {
33706
31898
  To: [PipelineExecutionError: Parameter `{thing}` is not defined],
33707
31899
  */
33708
31900
  if (!statementToEvaluate.includes(undefinedName + '(')) {
33709
- throw new PipelineExecutionError(spaceTrim$2((block) => `
31901
+ throw new PipelineExecutionError(_spaceTrim((block) => `
33710
31902
 
33711
31903
  Parameter \`{${undefinedName}}\` is not defined
33712
31904
 
@@ -33728,7 +31920,7 @@ class JavascriptEvalExecutionTools {
33728
31920
  `));
33729
31921
  }
33730
31922
  else {
33731
- throw new PipelineExecutionError(spaceTrim$2((block) => `
31923
+ throw new PipelineExecutionError(_spaceTrim((block) => `
33732
31924
  Function ${undefinedName}() is not defined
33733
31925
 
33734
31926
  - Make sure that the function is one of built-in functions
@@ -33867,7 +32059,7 @@ const knowledgeCommandParser = {
33867
32059
  */
33868
32060
  parse(input) {
33869
32061
  const { args } = input;
33870
- const knowledgeSourceContent = spaceTrim$2(args[0] || '');
32062
+ const knowledgeSourceContent = spaceTrim$1(args[0] || '');
33871
32063
  if (knowledgeSourceContent === '') {
33872
32064
  throw new ParseError(`Source is not defined`);
33873
32065
  }
@@ -34011,7 +32203,7 @@ const sectionCommandParser = {
34011
32203
  normalized = normalized.split('DIALOGUE').join('DIALOG');
34012
32204
  const taskTypes = SectionTypes.filter((sectionType) => normalized.includes(sectionType.split('_TASK').join('')));
34013
32205
  if (taskTypes.length !== 1) {
34014
- throw new ParseError(spaceTrim$2((block) => `
32206
+ throw new ParseError(spaceTrim$1((block) => `
34015
32207
  Unknown section type "${normalized}"
34016
32208
 
34017
32209
  Supported section types are:
@@ -34031,7 +32223,7 @@ const sectionCommandParser = {
34031
32223
  */
34032
32224
  $applyToTaskJson(command, $taskJson, $pipelineJson) {
34033
32225
  if ($taskJson.isSectionTypeSet === true) {
34034
- throw new ParseError(spaceTrim$2(`
32226
+ throw new ParseError(spaceTrim$1(`
34035
32227
  Section type is already defined in the section.
34036
32228
  It can be defined only once.
34037
32229
  `));
@@ -34311,7 +32503,7 @@ const expectCommandParser = {
34311
32503
  /**
34312
32504
  * Description of the FORMAT command
34313
32505
  */
34314
- description: spaceTrim$2(`
32506
+ description: spaceTrim$1(`
34315
32507
  Expect command describes the desired output of the task *(after post-processing)*
34316
32508
  It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.
34317
32509
  `),
@@ -34385,7 +32577,7 @@ const expectCommandParser = {
34385
32577
  }
34386
32578
  catch (error) {
34387
32579
  assertsError(error);
34388
- throw new ParseError(spaceTrim$2((block) => `
32580
+ throw new ParseError(spaceTrim$1((block) => `
34389
32581
  Invalid FORMAT command
34390
32582
  ${block(error.message)}:
34391
32583
  `));
@@ -34497,7 +32689,7 @@ function validateParameterName(parameterName) {
34497
32689
  if (!(error instanceof ParseError)) {
34498
32690
  throw error;
34499
32691
  }
34500
- throw new ParseError(spaceTrim$2((block) => `
32692
+ throw new ParseError(spaceTrim$1((block) => `
34501
32693
  ${block(error.message)}
34502
32694
 
34503
32695
  Tried to validate parameter name:
@@ -34556,7 +32748,7 @@ const foreachCommandParser = {
34556
32748
  const assignSign = args[3];
34557
32749
  const formatDefinition = FORMAT_DEFINITIONS.find((formatDefinition) => [formatDefinition.formatName, ...(formatDefinition.aliases || [])].includes(formatName));
34558
32750
  if (formatDefinition === undefined) {
34559
- throw new ParseError(spaceTrim$2((block) => `
32751
+ throw new ParseError(spaceTrim$1((block) => `
34560
32752
  Unsupported format "${formatName}"
34561
32753
 
34562
32754
  Available formats:
@@ -34568,7 +32760,7 @@ const foreachCommandParser = {
34568
32760
  }
34569
32761
  const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(subformatName));
34570
32762
  if (subvalueParser === undefined) {
34571
- throw new ParseError(spaceTrim$2((block) => `
32763
+ throw new ParseError(spaceTrim$1((block) => `
34572
32764
  Unsupported subformat name "${subformatName}" for format "${formatName}"
34573
32765
 
34574
32766
  Available subformat names for format "${formatDefinition.formatName}":
@@ -34616,7 +32808,7 @@ const foreachCommandParser = {
34616
32808
  outputSubparameterName = 'newLine';
34617
32809
  }
34618
32810
  else {
34619
- throw new ParseError(spaceTrim$2(`
32811
+ throw new ParseError(spaceTrim$1(`
34620
32812
  FOREACH ${formatName} ${subformatName} must specify output subparameter
34621
32813
 
34622
32814
  Correct example:
@@ -34692,7 +32884,7 @@ const formatCommandParser = {
34692
32884
  /**
34693
32885
  * Description of the FORMAT command
34694
32886
  */
34695
- description: spaceTrim$2(`
32887
+ description: spaceTrim$1(`
34696
32888
  Format command describes the desired output of the task (after post-processing)
34697
32889
  It can set limits for the maximum/minimum length of the output, measured in characters, words, sentences, paragraphs or some other shape of the output.
34698
32890
  `),
@@ -35064,7 +33256,7 @@ const formfactorCommandParser = {
35064
33256
  const formfactorNameCandidate = args[0].toUpperCase();
35065
33257
  const formfactor = FORMFACTOR_DEFINITIONS.find((definition) => [definition.name, ...{ aliasNames: [], ...definition }.aliasNames].includes(formfactorNameCandidate));
35066
33258
  if (formfactor === undefined) {
35067
- throw new ParseError(spaceTrim$2((block) => `
33259
+ throw new ParseError(spaceTrim$1((block) => `
35068
33260
  Unknown formfactor name "${formfactorNameCandidate}"
35069
33261
 
35070
33262
  Available formfactors:
@@ -35083,7 +33275,7 @@ const formfactorCommandParser = {
35083
33275
  */
35084
33276
  $applyToPipelineJson(command, $pipelineJson) {
35085
33277
  if ($pipelineJson.formfactorName !== undefined && $pipelineJson.formfactorName !== command.formfactorName) {
35086
- throw new ParseError(spaceTrim$2(`
33278
+ throw new ParseError(spaceTrim$1(`
35087
33279
  Redefinition of \`FORMFACTOR\` in the pipeline head
35088
33280
 
35089
33281
  You have used:
@@ -35231,7 +33423,7 @@ const modelCommandParser = {
35231
33423
  */
35232
33424
  parse(input) {
35233
33425
  const { args, normalized } = input;
35234
- const availableVariantsMessage = spaceTrim$2((block) => `
33426
+ const availableVariantsMessage = spaceTrim$1((block) => `
35235
33427
  Available variants are:
35236
33428
  ${block(MODEL_VARIANTS.map((variantName) => `- ${variantName}${variantName !== 'EMBEDDING' ? '' : ' (Not available in pipeline)'}`).join('\n'))}
35237
33429
  `);
@@ -35253,14 +33445,14 @@ const modelCommandParser = {
35253
33445
  // <- Note: [🤖]
35254
33446
  }
35255
33447
  else if (normalized.startsWith('MODEL_VARIANT_EMBED')) {
35256
- spaceTrim$2((block) => `
33448
+ spaceTrim$1((block) => `
35257
33449
  Embedding model can not be used in pipeline
35258
33450
 
35259
33451
  ${block(availableVariantsMessage)}
35260
33452
  `);
35261
33453
  }
35262
33454
  else {
35263
- throw new ParseError(spaceTrim$2((block) => `
33455
+ throw new ParseError(spaceTrim$1((block) => `
35264
33456
  Unknown model variant in command:
35265
33457
 
35266
33458
  ${block(availableVariantsMessage)}
@@ -35275,7 +33467,7 @@ const modelCommandParser = {
35275
33467
  };
35276
33468
  }
35277
33469
  else {
35278
- throw new ParseError(spaceTrim$2((block) => `
33470
+ throw new ParseError(spaceTrim$1((block) => `
35279
33471
  Unknown model key in command.
35280
33472
 
35281
33473
  Supported model keys are:
@@ -35302,7 +33494,7 @@ const modelCommandParser = {
35302
33494
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
35303
33495
  }
35304
33496
  else {
35305
- throw new ParseError(spaceTrim$2(`
33497
+ throw new ParseError(spaceTrim$1(`
35306
33498
  Redefinition of \`MODEL ${command.key}\` in the pipeline head
35307
33499
 
35308
33500
  You have used:
@@ -35330,7 +33522,7 @@ const modelCommandParser = {
35330
33522
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
35331
33523
  }
35332
33524
  else {
35333
- throw new ParseError(spaceTrim$2(`
33525
+ throw new ParseError(spaceTrim$1(`
35334
33526
  Redefinition of MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}"
35335
33527
 
35336
33528
  You have used:
@@ -35340,7 +33532,7 @@ const modelCommandParser = {
35340
33532
  }
35341
33533
  }
35342
33534
  if (command.value === ($pipelineJson.defaultModelRequirements || {})[command.key]) {
35343
- console.log(spaceTrim$2(`
33535
+ console.log(spaceTrim$1(`
35344
33536
  Setting MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}" to the same value as in the pipeline head
35345
33537
 
35346
33538
  In pipeline head:
@@ -35423,7 +33615,7 @@ const parameterCommandParser = {
35423
33615
  // <- TODO: When [🥶] fixed, change to:
35424
33616
  // > const parameterDescriptionRaw = rawArgs.split(parameterNameRaw).join('').trim();
35425
33617
  if (parameterDescriptionRaw && parameterDescriptionRaw.match(/\{(?<embeddedParameterName>[a-z0-9_]+)\}/im)) {
35426
- throw new ParseError(spaceTrim$2((block) => `
33618
+ throw new ParseError(spaceTrim$1((block) => `
35427
33619
  Parameter \`{${parameterNameRaw}}\` can not contain another parameter in description
35428
33620
 
35429
33621
  The description:
@@ -35605,7 +33797,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
35605
33797
  persona.description = personaDescription;
35606
33798
  return;
35607
33799
  }
35608
- console.warn(spaceTrim$2(`
33800
+ console.warn(spaceTrim$1(`
35609
33801
 
35610
33802
  Persona "${personaName}" is defined multiple times with different description:
35611
33803
 
@@ -35616,7 +33808,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
35616
33808
  ${personaDescription}
35617
33809
 
35618
33810
  `));
35619
- persona.description += spaceTrim$2('\n\n' + personaDescription);
33811
+ persona.description += spaceTrim$1('\n\n' + personaDescription);
35620
33812
  }
35621
33813
 
35622
33814
  /**
@@ -36457,7 +34649,7 @@ function removeMarkdownComments(content) {
36457
34649
  */
36458
34650
  function isFlatPipeline(pipelineString) {
36459
34651
  pipelineString = removeMarkdownComments(pipelineString);
36460
- pipelineString = spaceTrim$2(pipelineString);
34652
+ pipelineString = spaceTrim$1(pipelineString);
36461
34653
  const isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
36462
34654
  //const isLastLineReturnStatement = pipelineString.split(/\r?\n/).pop()!.split('`').join('').startsWith('->');
36463
34655
  const isBacktickBlockUsed = pipelineString.includes('```');
@@ -36483,7 +34675,7 @@ function deflatePipeline(pipelineString) {
36483
34675
  if (!isFlatPipeline(pipelineString)) {
36484
34676
  return pipelineString;
36485
34677
  }
36486
- pipelineString = spaceTrim$2(pipelineString);
34678
+ pipelineString = spaceTrim$1(pipelineString);
36487
34679
  const pipelineStringLines = pipelineString.split(/\r?\n/);
36488
34680
  const potentialReturnStatement = pipelineStringLines.pop();
36489
34681
  let returnStatement;
@@ -36496,19 +34688,19 @@ function deflatePipeline(pipelineString) {
36496
34688
  returnStatement = `-> {${DEFAULT_BOOK_OUTPUT_PARAMETER_NAME}}`;
36497
34689
  pipelineStringLines.push(potentialReturnStatement);
36498
34690
  }
36499
- const prompt = spaceTrim$2(pipelineStringLines.join('\n'));
34691
+ const prompt = spaceTrim$1(pipelineStringLines.join('\n'));
36500
34692
  let quotedPrompt;
36501
34693
  if (prompt.split(/\r?\n/).length <= 1) {
36502
34694
  quotedPrompt = `> ${prompt}`;
36503
34695
  }
36504
34696
  else {
36505
- quotedPrompt = spaceTrim$2((block) => `
34697
+ quotedPrompt = spaceTrim$1((block) => `
36506
34698
  \`\`\`
36507
34699
  ${block(prompt.split('`').join('\\`'))}
36508
34700
  \`\`\`
36509
34701
  `);
36510
34702
  }
36511
- pipelineString = validatePipelineString(spaceTrim$2((block) => `
34703
+ pipelineString = validatePipelineString(spaceTrim$1((block) => `
36512
34704
  # ${DEFAULT_BOOK_TITLE}
36513
34705
 
36514
34706
  ## Prompt
@@ -36566,7 +34758,7 @@ function parseMarkdownSection(value) {
36566
34758
  }
36567
34759
  const title = lines[0].replace(/^#+\s*/, '');
36568
34760
  const level = (_b = (_a = lines[0].match(/^#+/)) === null || _a === void 0 ? void 0 : _a[0].length) !== null && _b !== void 0 ? _b : 0;
36569
- const content = spaceTrim$2(lines.slice(1).join('\n'));
34761
+ const content = spaceTrim$1(lines.slice(1).join('\n'));
36570
34762
  if (level < 1 || level > 6) {
36571
34763
  throw new ParseError('Markdown section must have heading level between 1 and 6');
36572
34764
  }
@@ -36594,7 +34786,7 @@ function splitMarkdownIntoSections(markdown) {
36594
34786
  if (buffer.length === 0) {
36595
34787
  return;
36596
34788
  }
36597
- let section = spaceTrim$2(buffer.join('\n'));
34789
+ let section = spaceTrim$1(buffer.join('\n'));
36598
34790
  if (section === '') {
36599
34791
  return;
36600
34792
  }
@@ -36669,7 +34861,7 @@ function flattenMarkdown(markdown) {
36669
34861
  flattenedMarkdown += `## ${title}` + `\n\n`;
36670
34862
  flattenedMarkdown += content + `\n\n`; // <- [🧠] Maybe 3 new lines?
36671
34863
  }
36672
- return spaceTrim$2(flattenedMarkdown);
34864
+ return spaceTrim$1(flattenedMarkdown);
36673
34865
  }
36674
34866
  /**
36675
34867
  * TODO: [🏛] This can be part of markdown builder
@@ -37376,7 +35568,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
37376
35568
  catch (error) {
37377
35569
  assertsError(error);
37378
35570
  // TODO: [7] DRY
37379
- const wrappedErrorMessage = spaceTrim$2((block) => `
35571
+ const wrappedErrorMessage = spaceTrim$1((block) => `
37380
35572
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
37381
35573
 
37382
35574
  Original error message:
@@ -37411,7 +35603,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
37411
35603
  pipeline = { ...pipeline, pipelineUrl };
37412
35604
  }
37413
35605
  else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
37414
- throw new PipelineUrlError(spaceTrim$2(`
35606
+ throw new PipelineUrlError(spaceTrim$1(`
37415
35607
  Pipeline with URL ${pipeline.pipelineUrl} is not a child of the root URL ${rootUrl} 🍏
37416
35608
 
37417
35609
  File:
@@ -37449,7 +35641,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
37449
35641
  }
37450
35642
  else {
37451
35643
  const existing = collection.get(pipeline.pipelineUrl);
37452
- throw new PipelineUrlError(spaceTrim$2(`
35644
+ throw new PipelineUrlError(spaceTrim$1(`
37453
35645
  Pipeline with URL ${pipeline.pipelineUrl} is already in the collection 🍏
37454
35646
 
37455
35647
  Conflicting files:
@@ -37467,7 +35659,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
37467
35659
  catch (error) {
37468
35660
  assertsError(error);
37469
35661
  // TODO: [7] DRY
37470
- const wrappedErrorMessage = spaceTrim$2((block) => `
35662
+ const wrappedErrorMessage = spaceTrim$1((block) => `
37471
35663
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
37472
35664
 
37473
35665
  Original error message:
@@ -37643,7 +35835,7 @@ async function $getCompiledBook(tools, pipelineSource, options) {
37643
35835
  // console.log(`Strategy 3️⃣`);
37644
35836
  const response = await fetch(pipelineSource);
37645
35837
  if (response.status >= 300) {
37646
- throw new NotFoundError(spaceTrim$2((block) => `
35838
+ throw new NotFoundError(spaceTrim$1((block) => `
37647
35839
  Book not found on URL:
37648
35840
  ${block(pipelineSource)}
37649
35841
 
@@ -37653,7 +35845,7 @@ async function $getCompiledBook(tools, pipelineSource, options) {
37653
35845
  const pipelineString = await response.text();
37654
35846
  // console.log({ pipelineString });
37655
35847
  if (!isValidPipelineString(pipelineString)) {
37656
- throw new NotFoundError(spaceTrim$2((block) => `
35848
+ throw new NotFoundError(spaceTrim$1((block) => `
37657
35849
  Book not found on URL:
37658
35850
  ${block(pipelineSource)}
37659
35851
 
@@ -37675,7 +35867,7 @@ async function $getCompiledBook(tools, pipelineSource, options) {
37675
35867
  });
37676
35868
  return pipelineJson;
37677
35869
  } /* not else */
37678
- throw new NotFoundError(spaceTrim$2((block) => `
35870
+ throw new NotFoundError(spaceTrim$1((block) => `
37679
35871
  Book not found:
37680
35872
  ${block(pipelineSource)}
37681
35873
 
@@ -37857,7 +36049,7 @@ function validateBook(source) {
37857
36049
  * @deprecated Use `$generateBookBoilerplate` instead
37858
36050
  * @public exported from `@promptbook/core`
37859
36051
  */
37860
- padBook(validateBook(spaceTrim$2(`
36052
+ padBook(validateBook(spaceTrim$1(`
37861
36053
  AI Avatar
37862
36054
 
37863
36055
  PERSONA A friendly AI assistant that helps you with your tasks
@@ -37884,7 +36076,7 @@ function book(strings, ...values) {
37884
36076
  const bookString = prompt(strings, ...values).toString();
37885
36077
  if (!isValidPipelineString(bookString)) {
37886
36078
  // TODO: Make the CustomError for this
37887
- throw new Error(spaceTrim$2(`
36079
+ throw new Error(spaceTrim$1(`
37888
36080
  The string is not a valid pipeline string
37889
36081
 
37890
36082
  book\`
@@ -37894,7 +36086,7 @@ function book(strings, ...values) {
37894
36086
  }
37895
36087
  if (!isValidBook(bookString)) {
37896
36088
  // TODO: Make the CustomError for this
37897
- throw new Error(spaceTrim$2(`
36089
+ throw new Error(spaceTrim$1(`
37898
36090
  The string is not a valid book
37899
36091
 
37900
36092
  book\`
@@ -38820,7 +37012,7 @@ function promptbookifyAiText(text) {
38820
37012
  * TODO: [🧠][✌️] Make some Promptbook-native token system
38821
37013
  */
38822
37014
 
38823
- const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5-mini-2025-08-07';
37015
+ const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-nano';
38824
37016
  /**
38825
37017
  * Creates one structured log entry for streamed tool-call updates.
38826
37018
  *
@@ -39315,7 +37507,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
39315
37507
  }),
39316
37508
  ],
39317
37509
  };
39318
- const errorMessage = spaceTrim$2((block) => `
37510
+ const errorMessage = spaceTrim$1((block) => `
39319
37511
 
39320
37512
  The invoked tool \`${functionName}\` failed with error:
39321
37513
 
@@ -40265,7 +38457,7 @@ class SelfLearningManager {
40265
38457
  if (isJsonSchemaResponseFormat(responseFormat)) {
40266
38458
  const jsonSchema = responseFormat.json_schema;
40267
38459
  const schemaJson = JSON.stringify(jsonSchema, null, 4);
40268
- userMessageContent = spaceTrim$2((block) => `
38460
+ userMessageContent = spaceTrim$1((block) => `
40269
38461
  ${block(prompt.content)}
40270
38462
 
40271
38463
  NOTE Request was made through OpenAI Compatible API with \`response_format\` of type \`json_schema\` with the following schema:
@@ -40296,12 +38488,12 @@ class SelfLearningManager {
40296
38488
  const formattedAgentMessage = formatAgentMessageForJsonMode(result.content, usesJsonSchemaMode);
40297
38489
  const teacherInstructions = extractOpenTeacherInstructions(agentSource);
40298
38490
  const teacherInstructionsSection = teacherInstructions
40299
- ? spaceTrim$2((block) => `
38491
+ ? spaceTrim$1((block) => `
40300
38492
  **Teacher instructions:**
40301
38493
  ${block(teacherInstructions)}
40302
38494
  `)
40303
38495
  : '';
40304
- const teacherPromptContent = spaceTrim$2((block) => `
38496
+ const teacherPromptContent = spaceTrim$1((block) => `
40305
38497
 
40306
38498
  You are a teacher agent helping another agent to learn from its interactions.
40307
38499
 
@@ -40334,7 +38526,7 @@ class SelfLearningManager {
40334
38526
  ? '- This interaction used JSON mode, so the agent answer should stay as a formatted JSON code block.'
40335
38527
  : ''}
40336
38528
  ${block(isInitialMessageMissing
40337
- ? spaceTrim$2(`
38529
+ ? spaceTrim$1(`
40338
38530
  - The agent source does not have an INITIAL MESSAGE defined, generate one.
40339
38531
  - The INITIAL MESSAGE should be welcoming, informative about the agent capabilities and also should give some quick options to start the conversation with the agent.
40340
38532
  - The quick option looks like \`[👋 Hello](?message=Hello, how are you?)\`
@@ -40377,7 +38569,7 @@ class SelfLearningManager {
40377
38569
  */
40378
38570
  appendToAgentSource(section) {
40379
38571
  const currentSource = this.options.getAgentSource();
40380
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + section));
38572
+ const newSource = padBook(validateBook(spaceTrim$1(currentSource) + section));
40381
38573
  this.options.updateAgentSource(newSource);
40382
38574
  }
40383
38575
  }
@@ -40405,13 +38597,13 @@ function formatAgentMessageForJsonMode(content, isJsonMode) {
40405
38597
  }
40406
38598
  const parsedJson = tryParseJson(content);
40407
38599
  if (parsedJson === null) {
40408
- return spaceTrim$2((block) => `
38600
+ return spaceTrim$1((block) => `
40409
38601
  \`\`\`json
40410
38602
  ${block(content)}
40411
38603
  \`\`\`
40412
38604
  `);
40413
38605
  }
40414
- return spaceTrim$2((block) => `
38606
+ return spaceTrim$1((block) => `
40415
38607
  \`\`\`json
40416
38608
  ${block(JSON.stringify(parsedJson, null, 4))}
40417
38609
  \`\`\`
@@ -40443,7 +38635,7 @@ function formatSelfLearningSample(options) {
40443
38635
  const internalMessagesSection = options.internalMessages
40444
38636
  .map((internalMessage) => formatInternalLearningMessage(internalMessage))
40445
38637
  .join('\n\n');
40446
- return spaceTrim$2((block) => `
38638
+ return spaceTrim$1((block) => `
40447
38639
 
40448
38640
  USER MESSAGE
40449
38641
  ${block(options.userMessageContent)}
@@ -40461,7 +38653,7 @@ function formatSelfLearningSample(options) {
40461
38653
  * @private function of Agent
40462
38654
  */
40463
38655
  function formatInternalLearningMessage(internalMessage) {
40464
- return spaceTrim$2((block) => `
38656
+ return spaceTrim$1((block) => `
40465
38657
  INTERNAL MESSAGE
40466
38658
  ${block(stringifyInternalLearningPayload(internalMessage))}
40467
38659
  `);
@@ -40927,7 +39119,7 @@ function buildRemoteAgentSource(profile, meta) {
40927
39119
  .filter((line) => Boolean(line))
40928
39120
  .join('\n');
40929
39121
  const personaBlock = profile.personaDescription
40930
- ? spaceTrim$2((block) => `
39122
+ ? spaceTrim$1((block) => `
40931
39123
  PERSONA
40932
39124
  ${block(profile.personaDescription || '')}
40933
39125
  `)
@@ -40963,7 +39155,7 @@ class RemoteAgent extends Agent {
40963
39155
  // <- TODO: [🐱‍🚀] What about closed-source agents?
40964
39156
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
40965
39157
  if (!profileResponse.ok) {
40966
- throw new Error(spaceTrim$2((block) => `
39158
+ throw new Error(spaceTrim$1((block) => `
40967
39159
  Failed to fetch remote agent profile:
40968
39160
 
40969
39161
  Agent URL: