@promptbook/node 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 +969 -2797
  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 +933 -2758
  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,17 +1,14 @@
1
1
  import colors from 'colors';
2
2
  import { stat, access, constants, readFile, writeFile, readdir, mkdir, watch, rm, unlink } from 'fs/promises';
3
3
  import { basename, join, dirname, isAbsolute, relative } from 'path';
4
- import spaceTrim$2, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
+ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
5
5
  import JSZip from 'jszip';
6
- import { randomBytes, randomUUID } from 'crypto';
6
+ import { randomBytes } from 'crypto';
7
7
  import { SHA256 } from 'crypto-js';
8
8
  import hexEncoder from 'crypto-js/enc-hex';
9
9
  import { Readability } from '@mozilla/readability';
10
10
  import { JSDOM } from 'jsdom';
11
11
  import { Converter } from 'showdown';
12
- import { tmpdir } from 'os';
13
- import { ConfigChecker } from 'configchecker';
14
- import { chromium } from 'playwright';
15
12
  import { spawn } from 'child_process';
16
13
  import { forTime } from 'waitasecond';
17
14
  import * as dotenv from 'dotenv';
@@ -38,7 +35,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
38
35
  * @generated
39
36
  * @see https://github.com/webgptorg/promptbook
40
37
  */
41
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-12';
38
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-15';
42
39
  /**
43
40
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
44
41
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1216,7 +1213,7 @@ true);
1216
1213
  function getErrorReportUrl(error) {
1217
1214
  const report = {
1218
1215
  title: `🐜 Error report from ${NAME}`,
1219
- body: spaceTrim$2((block) => `
1216
+ body: spaceTrim$1((block) => `
1220
1217
 
1221
1218
 
1222
1219
  \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
@@ -1289,7 +1286,7 @@ function jsonParse(value) {
1289
1286
  }
1290
1287
  else if (typeof value !== 'string') {
1291
1288
  console.error('Can not parse JSON from non-string value.', { text: value });
1292
- throw new Error(spaceTrim$2(`
1289
+ throw new Error(spaceTrim$1(`
1293
1290
  Can not parse JSON from non-string value.
1294
1291
 
1295
1292
  The value type: ${typeof value}
@@ -1303,7 +1300,7 @@ function jsonParse(value) {
1303
1300
  if (!(error instanceof Error)) {
1304
1301
  throw error;
1305
1302
  }
1306
- throw new Error(spaceTrim$2((block) => `
1303
+ throw new Error(spaceTrim$1((block) => `
1307
1304
  ${block(error.message)}
1308
1305
 
1309
1306
  The expected JSON text:
@@ -1452,7 +1449,7 @@ function checkSerializableAsJson(options) {
1452
1449
  }
1453
1450
  else if (typeof value === 'object') {
1454
1451
  if (value instanceof Date) {
1455
- throw new UnexpectedError(spaceTrim$2((block) => `
1452
+ throw new UnexpectedError(spaceTrim$1((block) => `
1456
1453
  \`${name}\` is Date
1457
1454
 
1458
1455
  Use \`string_date_iso8601\` instead
@@ -1471,7 +1468,7 @@ function checkSerializableAsJson(options) {
1471
1468
  throw new UnexpectedError(`${name} is RegExp`);
1472
1469
  }
1473
1470
  else if (value instanceof Error) {
1474
- throw new UnexpectedError(spaceTrim$2((block) => `
1471
+ throw new UnexpectedError(spaceTrim$1((block) => `
1475
1472
  \`${name}\` is unserialized Error
1476
1473
 
1477
1474
  Use function \`serializeError\`
@@ -1494,7 +1491,7 @@ function checkSerializableAsJson(options) {
1494
1491
  }
1495
1492
  catch (error) {
1496
1493
  assertsError(error);
1497
- throw new UnexpectedError(spaceTrim$2((block) => `
1494
+ throw new UnexpectedError(spaceTrim$1((block) => `
1498
1495
  \`${name}\` is not serializable
1499
1496
 
1500
1497
  ${block(error.stack || error.message)}
@@ -1526,7 +1523,7 @@ function checkSerializableAsJson(options) {
1526
1523
  }
1527
1524
  }
1528
1525
  else {
1529
- throw new UnexpectedError(spaceTrim$2((block) => `
1526
+ throw new UnexpectedError(spaceTrim$1((block) => `
1530
1527
  \`${name}\` is unknown type
1531
1528
 
1532
1529
  Additional message for \`${name}\`:
@@ -2432,7 +2429,7 @@ function pipelineJsonToString(pipelineJson) {
2432
2429
  pipelineString += '\n\n';
2433
2430
  pipelineString += '```' + contentLanguage;
2434
2431
  pipelineString += '\n';
2435
- pipelineString += spaceTrim$2(content);
2432
+ pipelineString += spaceTrim$1(content);
2436
2433
  // <- TODO: [main] !!3 Escape
2437
2434
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2438
2435
  pipelineString += '\n';
@@ -3014,7 +3011,7 @@ function serializeError(error) {
3014
3011
  const { name, message, stack } = error;
3015
3012
  const { id } = error;
3016
3013
  if (!Object.keys(ALL_ERRORS).includes(name)) {
3017
- console.error(spaceTrim$2((block) => `
3014
+ console.error(spaceTrim$1((block) => `
3018
3015
 
3019
3016
  Cannot serialize error with name "${name}"
3020
3017
 
@@ -3078,7 +3075,7 @@ function deserializeError(error, isStackAddedToMessage = true) {
3078
3075
  message = `${name}: ${message}`;
3079
3076
  }
3080
3077
  if (isStackAddedToMessage && stack !== undefined && stack !== '') {
3081
- message = spaceTrim$2((block) => `
3078
+ message = spaceTrim$1((block) => `
3082
3079
  ${block(message)}
3083
3080
 
3084
3081
  Original stack trace:
@@ -3765,7 +3762,7 @@ const CsvFormatParser = {
3765
3762
  const { value, outputParameterName, settings, mapCallback, onProgress } = options;
3766
3763
  const csv = csvParse(value, settings);
3767
3764
  if (csv.errors.length !== 0) {
3768
- throw new CsvFormatError(spaceTrim$2((block) => `
3765
+ throw new CsvFormatError(spaceTrim$1((block) => `
3769
3766
  CSV parsing error
3770
3767
 
3771
3768
  Error(s) from CSV parsing:
@@ -3810,7 +3807,7 @@ const CsvFormatParser = {
3810
3807
  const { value, settings, mapCallback, onProgress } = options;
3811
3808
  const csv = csvParse(value, settings);
3812
3809
  if (csv.errors.length !== 0) {
3813
- throw new CsvFormatError(spaceTrim$2((block) => `
3810
+ throw new CsvFormatError(spaceTrim$1((block) => `
3814
3811
  CSV parsing error
3815
3812
 
3816
3813
  Error(s) from CSV parsing:
@@ -4020,7 +4017,7 @@ function mapAvailableToExpectedParameters(options) {
4020
4017
  }
4021
4018
  // Phase 2️⃣: Non-matching mapping
4022
4019
  if (expectedParameterNames.size !== availableParametersNames.size) {
4023
- throw new PipelineExecutionError(spaceTrim$2((block) => `
4020
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4024
4021
  Can not map available parameters to expected parameters
4025
4022
 
4026
4023
  Mapped parameters:
@@ -4098,14 +4095,14 @@ class MultipleLlmExecutionTools {
4098
4095
  if (description === undefined) {
4099
4096
  return headLine;
4100
4097
  }
4101
- return spaceTrim$2((block) => `
4098
+ return spaceTrim$1((block) => `
4102
4099
  ${headLine}
4103
4100
 
4104
4101
  ${ /* <- Note: Indenting the description: */block(description)}
4105
4102
  `);
4106
4103
  })
4107
4104
  .join('\n\n');
4108
- return spaceTrim$2((block) => `
4105
+ return spaceTrim$1((block) => `
4109
4106
  Multiple LLM Providers:
4110
4107
 
4111
4108
  ${block(innerModelsTitlesAndDescriptions)}
@@ -4207,7 +4204,7 @@ class MultipleLlmExecutionTools {
4207
4204
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
4208
4205
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
4209
4206
  // 3) ...
4210
- spaceTrim$2((block) => `
4207
+ spaceTrim$1((block) => `
4211
4208
  All execution tools of ${this.title} failed:
4212
4209
 
4213
4210
  ${block(errors
@@ -4220,7 +4217,7 @@ class MultipleLlmExecutionTools {
4220
4217
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\` into ${this.title}`);
4221
4218
  }
4222
4219
  else {
4223
- throw new PipelineExecutionError(spaceTrim$2((block) => `
4220
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4224
4221
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}" into ${this.title}
4225
4222
 
4226
4223
  Available \`LlmExecutionTools\`:
@@ -4257,7 +4254,7 @@ class MultipleLlmExecutionTools {
4257
4254
  */
4258
4255
  function joinLlmExecutionTools(title, ...llmExecutionTools) {
4259
4256
  if (llmExecutionTools.length === 0) {
4260
- const warningMessage = spaceTrim$2(`
4257
+ const warningMessage = spaceTrim$1(`
4261
4258
  You have not provided any \`LlmExecutionTools\`
4262
4259
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
4263
4260
 
@@ -5347,7 +5344,7 @@ async function executeFormatSubvalues(options) {
5347
5344
  return /* not await */ executeAttempts({ ...options, logLlmCall });
5348
5345
  }
5349
5346
  if (jokerParameterNames.length !== 0) {
5350
- throw new UnexpectedError(spaceTrim$2((block) => `
5347
+ throw new UnexpectedError(spaceTrim$1((block) => `
5351
5348
  JOKER parameters are not supported together with FOREACH command
5352
5349
 
5353
5350
  [🧞‍♀️] This should be prevented in \`validatePipeline\`
@@ -5360,7 +5357,7 @@ async function executeFormatSubvalues(options) {
5360
5357
  if (formatDefinition === undefined) {
5361
5358
  throw new UnexpectedError(
5362
5359
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5363
- spaceTrim$2((block) => `
5360
+ spaceTrim$1((block) => `
5364
5361
  Unsupported format "${task.foreach.formatName}"
5365
5362
 
5366
5363
  Available formats:
@@ -5377,7 +5374,7 @@ async function executeFormatSubvalues(options) {
5377
5374
  if (subvalueParser === undefined) {
5378
5375
  throw new UnexpectedError(
5379
5376
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5380
- spaceTrim$2((block) => `
5377
+ spaceTrim$1((block) => `
5381
5378
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
5382
5379
 
5383
5380
  Available subformat names for format "${formatDefinition.formatName}":
@@ -5417,7 +5414,7 @@ async function executeFormatSubvalues(options) {
5417
5414
  if (!(error instanceof PipelineExecutionError)) {
5418
5415
  throw error;
5419
5416
  }
5420
- const highLevelError = new PipelineExecutionError(spaceTrim$2((block) => `
5417
+ const highLevelError = new PipelineExecutionError(spaceTrim$1((block) => `
5421
5418
  ${error.message}
5422
5419
 
5423
5420
  This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -5441,7 +5438,7 @@ async function executeFormatSubvalues(options) {
5441
5438
  ...options,
5442
5439
  priority: priority + index,
5443
5440
  parameters: allSubparameters,
5444
- pipelineIdentification: spaceTrim$2((block) => `
5441
+ pipelineIdentification: spaceTrim$1((block) => `
5445
5442
  ${block(pipelineIdentification)}
5446
5443
  Subparameter index: ${index}
5447
5444
  `),
@@ -5450,7 +5447,7 @@ async function executeFormatSubvalues(options) {
5450
5447
  }
5451
5448
  catch (error) {
5452
5449
  if (length > BIG_DATASET_TRESHOLD) {
5453
- console.error(spaceTrim$2((block) => `
5450
+ console.error(spaceTrim$1((block) => `
5454
5451
  ${error.message}
5455
5452
 
5456
5453
  This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -6622,14 +6619,14 @@ function $registeredScrapersMessage(availableScrapers) {
6622
6619
  return { ...metadata, isMetadataAviailable, isInstalled, isAvailableInTools };
6623
6620
  });
6624
6621
  if (metadata.length === 0) {
6625
- return spaceTrim$2(`
6622
+ return spaceTrim$1(`
6626
6623
  **No scrapers are available**
6627
6624
 
6628
6625
  This is a unexpected behavior, you are probably using some broken version of Promptbook
6629
6626
  At least there should be available the metadata of the scrapers
6630
6627
  `);
6631
6628
  }
6632
- return spaceTrim$2((block) => `
6629
+ return spaceTrim$1((block) => `
6633
6630
  Available scrapers are:
6634
6631
  ${block(metadata
6635
6632
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvailableInBrowser, isAvailableInTools, }, i) => {
@@ -6872,7 +6869,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
6872
6869
  else if (urlOrRequest instanceof Request) {
6873
6870
  url = urlOrRequest.url;
6874
6871
  }
6875
- throw new PromptbookFetchError(spaceTrim$2((block) => `
6872
+ throw new PromptbookFetchError(spaceTrim$1((block) => `
6876
6873
  Can not fetch "${url}"
6877
6874
 
6878
6875
  Fetch error:
@@ -7032,7 +7029,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
7032
7029
  const fileExtension = getFileExtension(filename);
7033
7030
  const mimeType = extensionToMimeType(fileExtension || '');
7034
7031
  if (!(await isFileExisting(filename, tools.fs))) {
7035
- throw new NotFoundError(spaceTrim$2((block) => `
7032
+ throw new NotFoundError(spaceTrim$1((block) => `
7036
7033
  Can not make source handler for file which does not exist:
7037
7034
 
7038
7035
  File:
@@ -7125,7 +7122,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
7125
7122
  // <- TODO: [🪓] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
7126
7123
  break;
7127
7124
  }
7128
- console.warn(spaceTrim$2((block) => `
7125
+ console.warn(spaceTrim$1((block) => `
7129
7126
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
7130
7127
 
7131
7128
  The source:
@@ -7141,7 +7138,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
7141
7138
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
7142
7139
  }
7143
7140
  if (partialPieces === null) {
7144
- throw new KnowledgeScrapeError(spaceTrim$2((block) => `
7141
+ throw new KnowledgeScrapeError(spaceTrim$1((block) => `
7145
7142
  Cannot scrape knowledge
7146
7143
 
7147
7144
  The source:
@@ -7477,7 +7474,7 @@ const knowledgeCommandParser = {
7477
7474
  */
7478
7475
  parse(input) {
7479
7476
  const { args } = input;
7480
- const knowledgeSourceContent = spaceTrim$2(args[0] || '');
7477
+ const knowledgeSourceContent = spaceTrim$1(args[0] || '');
7481
7478
  if (knowledgeSourceContent === '') {
7482
7479
  throw new ParseError(`Source is not defined`);
7483
7480
  }
@@ -7621,7 +7618,7 @@ const sectionCommandParser = {
7621
7618
  normalized = normalized.split('DIALOGUE').join('DIALOG');
7622
7619
  const taskTypes = SectionTypes.filter((sectionType) => normalized.includes(sectionType.split('_TASK').join('')));
7623
7620
  if (taskTypes.length !== 1) {
7624
- throw new ParseError(spaceTrim$2((block) => `
7621
+ throw new ParseError(spaceTrim$1((block) => `
7625
7622
  Unknown section type "${normalized}"
7626
7623
 
7627
7624
  Supported section types are:
@@ -7641,7 +7638,7 @@ const sectionCommandParser = {
7641
7638
  */
7642
7639
  $applyToTaskJson(command, $taskJson, $pipelineJson) {
7643
7640
  if ($taskJson.isSectionTypeSet === true) {
7644
- throw new ParseError(spaceTrim$2(`
7641
+ throw new ParseError(spaceTrim$1(`
7645
7642
  Section type is already defined in the section.
7646
7643
  It can be defined only once.
7647
7644
  `));
@@ -7993,7 +7990,7 @@ const expectCommandParser = {
7993
7990
  /**
7994
7991
  * Description of the FORMAT command
7995
7992
  */
7996
- description: spaceTrim$2(`
7993
+ description: spaceTrim$1(`
7997
7994
  Expect command describes the desired output of the task *(after post-processing)*
7998
7995
  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.
7999
7996
  `),
@@ -8067,7 +8064,7 @@ const expectCommandParser = {
8067
8064
  }
8068
8065
  catch (error) {
8069
8066
  assertsError(error);
8070
- throw new ParseError(spaceTrim$2((block) => `
8067
+ throw new ParseError(spaceTrim$1((block) => `
8071
8068
  Invalid FORMAT command
8072
8069
  ${block(error.message)}:
8073
8070
  `));
@@ -8257,7 +8254,7 @@ function validateParameterName(parameterName) {
8257
8254
  if (!(error instanceof ParseError)) {
8258
8255
  throw error;
8259
8256
  }
8260
- throw new ParseError(spaceTrim$2((block) => `
8257
+ throw new ParseError(spaceTrim$1((block) => `
8261
8258
  ${block(error.message)}
8262
8259
 
8263
8260
  Tried to validate parameter name:
@@ -8316,7 +8313,7 @@ const foreachCommandParser = {
8316
8313
  const assignSign = args[3];
8317
8314
  const formatDefinition = FORMAT_DEFINITIONS.find((formatDefinition) => [formatDefinition.formatName, ...(formatDefinition.aliases || [])].includes(formatName));
8318
8315
  if (formatDefinition === undefined) {
8319
- throw new ParseError(spaceTrim$2((block) => `
8316
+ throw new ParseError(spaceTrim$1((block) => `
8320
8317
  Unsupported format "${formatName}"
8321
8318
 
8322
8319
  Available formats:
@@ -8328,7 +8325,7 @@ const foreachCommandParser = {
8328
8325
  }
8329
8326
  const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(subformatName));
8330
8327
  if (subvalueParser === undefined) {
8331
- throw new ParseError(spaceTrim$2((block) => `
8328
+ throw new ParseError(spaceTrim$1((block) => `
8332
8329
  Unsupported subformat name "${subformatName}" for format "${formatName}"
8333
8330
 
8334
8331
  Available subformat names for format "${formatDefinition.formatName}":
@@ -8376,7 +8373,7 @@ const foreachCommandParser = {
8376
8373
  outputSubparameterName = 'newLine';
8377
8374
  }
8378
8375
  else {
8379
- throw new ParseError(spaceTrim$2(`
8376
+ throw new ParseError(spaceTrim$1(`
8380
8377
  FOREACH ${formatName} ${subformatName} must specify output subparameter
8381
8378
 
8382
8379
  Correct example:
@@ -8452,7 +8449,7 @@ const formatCommandParser = {
8452
8449
  /**
8453
8450
  * Description of the FORMAT command
8454
8451
  */
8455
- description: spaceTrim$2(`
8452
+ description: spaceTrim$1(`
8456
8453
  Format command describes the desired output of the task (after post-processing)
8457
8454
  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.
8458
8455
  `),
@@ -8824,7 +8821,7 @@ const formfactorCommandParser = {
8824
8821
  const formfactorNameCandidate = args[0].toUpperCase();
8825
8822
  const formfactor = FORMFACTOR_DEFINITIONS.find((definition) => [definition.name, ...{ aliasNames: [], ...definition }.aliasNames].includes(formfactorNameCandidate));
8826
8823
  if (formfactor === undefined) {
8827
- throw new ParseError(spaceTrim$2((block) => `
8824
+ throw new ParseError(spaceTrim$1((block) => `
8828
8825
  Unknown formfactor name "${formfactorNameCandidate}"
8829
8826
 
8830
8827
  Available formfactors:
@@ -8843,7 +8840,7 @@ const formfactorCommandParser = {
8843
8840
  */
8844
8841
  $applyToPipelineJson(command, $pipelineJson) {
8845
8842
  if ($pipelineJson.formfactorName !== undefined && $pipelineJson.formfactorName !== command.formfactorName) {
8846
- throw new ParseError(spaceTrim$2(`
8843
+ throw new ParseError(spaceTrim$1(`
8847
8844
  Redefinition of \`FORMFACTOR\` in the pipeline head
8848
8845
 
8849
8846
  You have used:
@@ -8991,7 +8988,7 @@ const modelCommandParser = {
8991
8988
  */
8992
8989
  parse(input) {
8993
8990
  const { args, normalized } = input;
8994
- const availableVariantsMessage = spaceTrim$2((block) => `
8991
+ const availableVariantsMessage = spaceTrim$1((block) => `
8995
8992
  Available variants are:
8996
8993
  ${block(MODEL_VARIANTS.map((variantName) => `- ${variantName}${variantName !== 'EMBEDDING' ? '' : ' (Not available in pipeline)'}`).join('\n'))}
8997
8994
  `);
@@ -9013,14 +9010,14 @@ const modelCommandParser = {
9013
9010
  // <- Note: [🤖]
9014
9011
  }
9015
9012
  else if (normalized.startsWith('MODEL_VARIANT_EMBED')) {
9016
- spaceTrim$2((block) => `
9013
+ spaceTrim$1((block) => `
9017
9014
  Embedding model can not be used in pipeline
9018
9015
 
9019
9016
  ${block(availableVariantsMessage)}
9020
9017
  `);
9021
9018
  }
9022
9019
  else {
9023
- throw new ParseError(spaceTrim$2((block) => `
9020
+ throw new ParseError(spaceTrim$1((block) => `
9024
9021
  Unknown model variant in command:
9025
9022
 
9026
9023
  ${block(availableVariantsMessage)}
@@ -9035,7 +9032,7 @@ const modelCommandParser = {
9035
9032
  };
9036
9033
  }
9037
9034
  else {
9038
- throw new ParseError(spaceTrim$2((block) => `
9035
+ throw new ParseError(spaceTrim$1((block) => `
9039
9036
  Unknown model key in command.
9040
9037
 
9041
9038
  Supported model keys are:
@@ -9062,7 +9059,7 @@ const modelCommandParser = {
9062
9059
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
9063
9060
  }
9064
9061
  else {
9065
- throw new ParseError(spaceTrim$2(`
9062
+ throw new ParseError(spaceTrim$1(`
9066
9063
  Redefinition of \`MODEL ${command.key}\` in the pipeline head
9067
9064
 
9068
9065
  You have used:
@@ -9090,7 +9087,7 @@ const modelCommandParser = {
9090
9087
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
9091
9088
  }
9092
9089
  else {
9093
- throw new ParseError(spaceTrim$2(`
9090
+ throw new ParseError(spaceTrim$1(`
9094
9091
  Redefinition of MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}"
9095
9092
 
9096
9093
  You have used:
@@ -9100,7 +9097,7 @@ const modelCommandParser = {
9100
9097
  }
9101
9098
  }
9102
9099
  if (command.value === ($pipelineJson.defaultModelRequirements || {})[command.key]) {
9103
- console.log(spaceTrim$2(`
9100
+ console.log(spaceTrim$1(`
9104
9101
  Setting MODEL \`${command.key}\` in the task "${$taskJson.title || $taskJson.name}" to the same value as in the pipeline head
9105
9102
 
9106
9103
  In pipeline head:
@@ -9183,7 +9180,7 @@ const parameterCommandParser = {
9183
9180
  // <- TODO: When [🥶] fixed, change to:
9184
9181
  // > const parameterDescriptionRaw = rawArgs.split(parameterNameRaw).join('').trim();
9185
9182
  if (parameterDescriptionRaw && parameterDescriptionRaw.match(/\{(?<embeddedParameterName>[a-z0-9_]+)\}/im)) {
9186
- throw new ParseError(spaceTrim$2((block) => `
9183
+ throw new ParseError(spaceTrim$1((block) => `
9187
9184
  Parameter \`{${parameterNameRaw}}\` can not contain another parameter in description
9188
9185
 
9189
9186
  The description:
@@ -9365,7 +9362,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
9365
9362
  persona.description = personaDescription;
9366
9363
  return;
9367
9364
  }
9368
- console.warn(spaceTrim$2(`
9365
+ console.warn(spaceTrim$1(`
9369
9366
 
9370
9367
  Persona "${personaName}" is defined multiple times with different description:
9371
9368
 
@@ -9376,7 +9373,7 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
9376
9373
  ${personaDescription}
9377
9374
 
9378
9375
  `));
9379
- persona.description += spaceTrim$2('\n\n' + personaDescription);
9376
+ persona.description += spaceTrim$1('\n\n' + personaDescription);
9380
9377
  }
9381
9378
 
9382
9379
  /**
@@ -10231,7 +10228,7 @@ function removeMarkdownComments(content) {
10231
10228
  */
10232
10229
  function isFlatPipeline(pipelineString) {
10233
10230
  pipelineString = removeMarkdownComments(pipelineString);
10234
- pipelineString = spaceTrim$2(pipelineString);
10231
+ pipelineString = spaceTrim$1(pipelineString);
10235
10232
  const isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
10236
10233
  //const isLastLineReturnStatement = pipelineString.split(/\r?\n/).pop()!.split('`').join('').startsWith('->');
10237
10234
  const isBacktickBlockUsed = pipelineString.includes('```');
@@ -10257,7 +10254,7 @@ function deflatePipeline(pipelineString) {
10257
10254
  if (!isFlatPipeline(pipelineString)) {
10258
10255
  return pipelineString;
10259
10256
  }
10260
- pipelineString = spaceTrim$2(pipelineString);
10257
+ pipelineString = spaceTrim$1(pipelineString);
10261
10258
  const pipelineStringLines = pipelineString.split(/\r?\n/);
10262
10259
  const potentialReturnStatement = pipelineStringLines.pop();
10263
10260
  let returnStatement;
@@ -10270,19 +10267,19 @@ function deflatePipeline(pipelineString) {
10270
10267
  returnStatement = `-> {${DEFAULT_BOOK_OUTPUT_PARAMETER_NAME}}`;
10271
10268
  pipelineStringLines.push(potentialReturnStatement);
10272
10269
  }
10273
- const prompt = spaceTrim$2(pipelineStringLines.join('\n'));
10270
+ const prompt = spaceTrim$1(pipelineStringLines.join('\n'));
10274
10271
  let quotedPrompt;
10275
10272
  if (prompt.split(/\r?\n/).length <= 1) {
10276
10273
  quotedPrompt = `> ${prompt}`;
10277
10274
  }
10278
10275
  else {
10279
- quotedPrompt = spaceTrim$2((block) => `
10276
+ quotedPrompt = spaceTrim$1((block) => `
10280
10277
  \`\`\`
10281
10278
  ${block(prompt.split('`').join('\\`'))}
10282
10279
  \`\`\`
10283
10280
  `);
10284
10281
  }
10285
- pipelineString = validatePipelineString(spaceTrim$2((block) => `
10282
+ pipelineString = validatePipelineString(spaceTrim$1((block) => `
10286
10283
  # ${DEFAULT_BOOK_TITLE}
10287
10284
 
10288
10285
  ## Prompt
@@ -10346,7 +10343,7 @@ function extractAllListItemsFromMarkdown(markdown) {
10346
10343
  function extractOneBlockFromMarkdown(markdown) {
10347
10344
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
10348
10345
  if (codeBlocks.length !== 1) {
10349
- throw new ParseError(spaceTrim$2((block) => `
10346
+ throw new ParseError(spaceTrim$1((block) => `
10350
10347
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
10351
10348
 
10352
10349
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -10371,7 +10368,7 @@ function parseMarkdownSection(value) {
10371
10368
  }
10372
10369
  const title = lines[0].replace(/^#+\s*/, '');
10373
10370
  const level = (_b = (_a = lines[0].match(/^#+/)) === null || _a === void 0 ? void 0 : _a[0].length) !== null && _b !== void 0 ? _b : 0;
10374
- const content = spaceTrim$2(lines.slice(1).join('\n'));
10371
+ const content = spaceTrim$1(lines.slice(1).join('\n'));
10375
10372
  if (level < 1 || level > 6) {
10376
10373
  throw new ParseError('Markdown section must have heading level between 1 and 6');
10377
10374
  }
@@ -10399,7 +10396,7 @@ function splitMarkdownIntoSections(markdown) {
10399
10396
  if (buffer.length === 0) {
10400
10397
  return;
10401
10398
  }
10402
- let section = spaceTrim$2(buffer.join('\n'));
10399
+ let section = spaceTrim$1(buffer.join('\n'));
10403
10400
  if (section === '') {
10404
10401
  return;
10405
10402
  }
@@ -10474,7 +10471,7 @@ function flattenMarkdown(markdown) {
10474
10471
  flattenedMarkdown += `## ${title}` + `\n\n`;
10475
10472
  flattenedMarkdown += content + `\n\n`; // <- [🧠] Maybe 3 new lines?
10476
10473
  }
10477
- return spaceTrim$2(flattenedMarkdown);
10474
+ return spaceTrim$1(flattenedMarkdown);
10478
10475
  }
10479
10476
  /**
10480
10477
  * TODO: [🏛] This can be part of markdown builder
@@ -11217,7 +11214,7 @@ function buildParametersSection(items) {
11217
11214
  const entries = items
11218
11215
  .flatMap((item) => formatParameterListItem(item).split(/\r?\n/))
11219
11216
  .filter((line) => line !== '');
11220
- return spaceTrim$2((block) => `
11217
+ return spaceTrim$1((block) => `
11221
11218
  **Parameters:**
11222
11219
  ${block(entries.join('\n'))}
11223
11220
 
@@ -11290,7 +11287,7 @@ function isPromptString(value) {
11290
11287
  */
11291
11288
  function prompt(strings, ...values) {
11292
11289
  if (values.length === 0) {
11293
- return new PromptString(spaceTrim$2(strings.join('')));
11290
+ return new PromptString(spaceTrim$1(strings.join('')));
11294
11291
  }
11295
11292
  const stringsWithHiddenParameters = strings.map((stringsItem) => ParameterEscaping.hideBrackets(stringsItem));
11296
11293
  const parameterMetadata = values.map((value) => {
@@ -11331,7 +11328,7 @@ function prompt(strings, ...values) {
11331
11328
  ? `${result}${stringsItem}`
11332
11329
  : `${result}${stringsItem}${ParameterSection.formatParameterPlaceholder(parameterName)}`;
11333
11330
  }, '');
11334
- pipelineString = spaceTrim$2(pipelineString);
11331
+ pipelineString = spaceTrim$1(pipelineString);
11335
11332
  try {
11336
11333
  pipelineString = templateParameters(pipelineString, parameters);
11337
11334
  }
@@ -11340,7 +11337,7 @@ function prompt(strings, ...values) {
11340
11337
  throw error;
11341
11338
  }
11342
11339
  console.error({ pipelineString, parameters, parameterNames: parameterNamesOrdered, error });
11343
- throw new UnexpectedError(spaceTrim$2((block) => `
11340
+ throw new UnexpectedError(spaceTrim$1((block) => `
11344
11341
  Internal error in prompt template literal
11345
11342
 
11346
11343
  ${block(JSON.stringify({ strings, values }, null, 4))}}
@@ -11481,7 +11478,7 @@ function $isRunningInWebWorker() {
11481
11478
  * @public exported from `@promptbook/utils`
11482
11479
  */
11483
11480
  function computeHash(value) {
11484
- return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
11481
+ return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
11485
11482
  }
11486
11483
  /**
11487
11484
  * TODO: [🥬][🥬] Use this ACRY
@@ -13445,8 +13442,8 @@ class MarkdownScraper {
13445
13442
  knowledgeTextPieces.map(async (knowledgeTextPiece, i) => {
13446
13443
  // Note: These are just default values, they will be overwritten by the actual values:
13447
13444
  let name = `piece-${i}`;
13448
- let title = spaceTrim$2(knowledgeTextPiece.substring(0, 100));
13449
- const knowledgePieceContent = spaceTrim$2(knowledgeTextPiece);
13445
+ let title = spaceTrim$1(knowledgeTextPiece.substring(0, 100));
13446
+ const knowledgePieceContent = spaceTrim$1(knowledgeTextPiece);
13450
13447
  let keywords = [];
13451
13448
  const index = [];
13452
13449
  /*
@@ -13459,7 +13456,7 @@ class MarkdownScraper {
13459
13456
  isCrashedOnError: true,
13460
13457
  });
13461
13458
  const { title: titleRaw = 'Untitled' } = titleResult.outputParameters;
13462
- title = spaceTrim$2(titleRaw) /* <- TODO: Maybe do in pipeline */;
13459
+ title = spaceTrim$1(titleRaw) /* <- TODO: Maybe do in pipeline */;
13463
13460
  name = titleToName(title);
13464
13461
  // --- Keywords
13465
13462
  const keywordsResult = await prepareKeywordsExecutor({ knowledgePieceContent }).asPromise({
@@ -13812,2673 +13809,345 @@ async function fetchUrlContent(url) {
13812
13809
  */
13813
13810
 
13814
13811
  /**
13815
- * Prompt parameter key used to pass hidden runtime context to tool execution.
13816
- *
13817
- * @private internal runtime wiring for commitment tools
13818
- */
13819
- const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
13820
- /**
13821
- * Hidden argument key used to pass runtime context into individual tool calls.
13822
- *
13823
- * @private internal runtime wiring for commitment tools
13824
- */
13825
- const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
13826
- /**
13827
- * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
13828
- *
13829
- * @private internal runtime wiring for commitment tools
13830
- */
13831
- const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
13832
- /**
13833
- * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
13834
- *
13835
- * @private internal runtime wiring for commitment tools
13836
- */
13837
- const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
13838
- /**
13839
- * Monotonic counter used for hidden progress-listener tokens.
13812
+ * Cached implementation of `run_browser` when it can be resolved.
13840
13813
  *
13841
- * @private internal runtime wiring for commitment tools
13814
+ * @private internal utility for USE BROWSER commitment
13842
13815
  */
13843
- let toolCallProgressListenerCounter = 0;
13816
+ let cachedRunBrowserTool = null;
13844
13817
  /**
13845
- * Active tool-progress listeners keyed by hidden execution token.
13818
+ * Cached loading error to avoid repeating expensive resolution attempts.
13846
13819
  *
13847
- * @private internal runtime wiring for commitment tools
13820
+ * @private internal utility for USE BROWSER commitment
13848
13821
  */
13849
- const toolCallProgressListeners = new Map();
13822
+ let cachedRunBrowserToolError = null;
13850
13823
  /**
13851
- * Parses unknown runtime context payload into a normalized object.
13824
+ * Attempts to load the server-side `run_browser` tool lazily.
13852
13825
  *
13853
- * @private internal runtime wiring for commitment tools
13826
+ * @returns Loaded `run_browser` implementation
13827
+ * @private internal utility for USE BROWSER commitment
13854
13828
  */
13855
- function parseToolRuntimeContext(rawValue) {
13856
- if (!rawValue) {
13857
- return null;
13829
+ function loadRunBrowserToolForNode() {
13830
+ if (cachedRunBrowserTool !== null) {
13831
+ return cachedRunBrowserTool;
13858
13832
  }
13859
- let parsed = rawValue;
13860
- if (typeof rawValue === 'string') {
13861
- try {
13862
- parsed = JSON.parse(rawValue);
13863
- }
13864
- catch (_a) {
13865
- return null;
13833
+ if (cachedRunBrowserToolError !== null) {
13834
+ throw cachedRunBrowserToolError;
13835
+ }
13836
+ try {
13837
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13838
+ const runBrowserModule = require('../../../apps/agents-server/src/tools/run_browser');
13839
+ if (typeof runBrowserModule.run_browser !== 'function') {
13840
+ throw new Error('run_browser value is not a function but ' + typeof runBrowserModule.run_browser);
13866
13841
  }
13842
+ cachedRunBrowserTool = runBrowserModule.run_browser;
13843
+ return cachedRunBrowserTool;
13867
13844
  }
13868
- if (!parsed || typeof parsed !== 'object') {
13869
- return null;
13845
+ catch (error) {
13846
+ assertsError(error);
13847
+ cachedRunBrowserToolError = error;
13848
+ throw error;
13870
13849
  }
13871
- return parsed;
13872
- }
13873
- /**
13874
- * Reads runtime context attached to tool call arguments.
13875
- *
13876
- * @private internal runtime wiring for commitment tools
13877
- */
13878
- function readToolRuntimeContextFromToolArgs(args) {
13879
- return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
13880
13850
  }
13881
13851
  /**
13882
- * Reads the hidden tool-progress token from tool arguments.
13852
+ * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
13883
13853
  *
13884
- * @private internal runtime wiring for commitment tools
13885
- */
13886
- function readToolProgressTokenFromToolArgs(args) {
13887
- const token = args[TOOL_PROGRESS_TOKEN_ARGUMENT];
13888
- return typeof token === 'string' && token.trim().length > 0 ? token : null;
13889
- }
13890
- /**
13891
- * Serializes runtime context for prompt parameters.
13854
+ * This uses fully lazy resolution to keep CLI startup independent from optional browser tooling.
13855
+ * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
13892
13856
  *
13893
- * @private internal runtime wiring for commitment tools
13857
+ * @private internal utility for USE BROWSER commitment
13894
13858
  */
13895
- function serializeToolRuntimeContext(context) {
13896
- return JSON.stringify(context);
13859
+ function resolveRunBrowserToolForNode() {
13860
+ return async (args) => {
13861
+ try {
13862
+ const runBrowserTool = loadRunBrowserToolForNode();
13863
+ return await runBrowserTool(args);
13864
+ }
13865
+ catch (error) {
13866
+ assertsError(error);
13867
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
13868
+ \`run_browser\` tool is not available in this environment.
13869
+ This commitment requires the Agents Server browser runtime with Playwright CLI.
13870
+
13871
+ ${error.name}:
13872
+ ${block(error.message)}
13873
+ `));
13874
+ }
13875
+ };
13897
13876
  }
13877
+
13898
13878
  /**
13899
- * Registers one in-memory listener that receives progress updates emitted by a running tool.
13879
+ * Resolves the server-side implementation of the send_email tool for Node.js environments.
13900
13880
  *
13901
- * The returned token is passed into script execution as a hidden argument so tool implementations
13902
- * can stream progress without exposing extra parameters to the model.
13881
+ * This uses a lazy require so the core package can still load even if the Agents Server
13882
+ * module is unavailable. When the server tool cannot be resolved, a fallback implementation
13883
+ * throws a helpful error message.
13903
13884
  *
13904
- * @param listener - Listener notified about tool progress.
13905
- * @returns Hidden token used to route progress updates.
13906
- * @private internal runtime wiring for commitment tools
13885
+ * @private internal utility for USE EMAIL commitment
13907
13886
  */
13908
- function registerToolCallProgressListener(listener) {
13909
- toolCallProgressListenerCounter += 1;
13910
- const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
13911
- toolCallProgressListeners.set(token, listener);
13912
- return token;
13887
+ function resolveSendEmailToolForNode() {
13888
+ try {
13889
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13890
+ const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
13891
+ if (typeof send_email !== 'function') {
13892
+ throw new Error('send_email value is not a function but ' + typeof send_email);
13893
+ }
13894
+ return send_email;
13895
+ }
13896
+ catch (error) {
13897
+ const normalizedError = error instanceof Error
13898
+ ? error
13899
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
13900
+ return async () => {
13901
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
13902
+ \`send_email\` tool is not available in this environment.
13903
+ This commitment requires Agents Server runtime with wallet-backed SMTP sending.
13904
+
13905
+ ${normalizedError.name}:
13906
+ ${block(normalizedError.message)}
13907
+ `));
13908
+ };
13909
+ }
13913
13910
  }
13911
+
13914
13912
  /**
13915
- * Unregisters one in-memory progress listener.
13913
+ * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
13916
13914
  *
13917
- * @param token - Token previously created by `registerToolCallProgressListener`.
13918
- * @private internal runtime wiring for commitment tools
13919
- */
13920
- function unregisterToolCallProgressListener(token) {
13921
- toolCallProgressListeners.delete(token);
13922
- }
13923
- /**
13924
- * Emits one tool progress update using a hidden token carried in tool arguments.
13915
+ * Uses lazy require so core package can load outside Agents Server.
13925
13916
  *
13926
- * @param args - Raw tool arguments including hidden runtime keys.
13927
- * @param update - Incremental progress update.
13928
- * @returns `true` when a listener was found and notified.
13929
- * @private internal runtime wiring for commitment tools
13917
+ * @private internal utility for USE SPAWN commitment
13930
13918
  */
13931
- function emitToolCallProgressFromToolArgs(args, update) {
13932
- const token = readToolProgressTokenFromToolArgs(args);
13933
- if (!token) {
13934
- return false;
13919
+ function resolveSpawnAgentToolForNode() {
13920
+ try {
13921
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13922
+ const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
13923
+ if (typeof spawn_agent !== 'function') {
13924
+ throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
13925
+ }
13926
+ return spawn_agent;
13935
13927
  }
13936
- const listener = toolCallProgressListeners.get(token);
13937
- if (!listener) {
13938
- return false;
13928
+ catch (error) {
13929
+ const normalizedError = error instanceof Error
13930
+ ? error
13931
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
13932
+ return async () => {
13933
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
13934
+ \`spawn_agent\` tool is not available in this environment.
13935
+ This commitment requires Agents Server runtime with agent persistence enabled.
13936
+
13937
+ ${normalizedError.name}:
13938
+ ${block(normalizedError.message)}
13939
+ `));
13940
+ };
13939
13941
  }
13940
- listener(update);
13941
- return true;
13942
13942
  }
13943
- /**
13944
- * Note: [💞] Ignore a discrepancy between file name and entity name
13945
- */
13946
13943
 
13947
13944
  /**
13948
- * Logical public directory marker used in `run_browser` payload paths.
13945
+ * Generates a regex pattern to match a specific commitment
13949
13946
  *
13950
- * This value is kept stable for UI parsing and `/api/browser-artifacts/*` URL mapping.
13951
- */
13952
- const RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY = '.playwright-cli';
13953
- /**
13954
- * Runtime environment variable that overrides local artifact storage directory.
13955
- */
13956
- const RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV = 'RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY';
13957
- /**
13958
- * Default writable directory for `run_browser` screenshot/video artifacts.
13959
- */
13960
- const DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY = join(tmpdir(), 'promptbook', 'run-browser-artifacts');
13961
- /**
13962
- * Converts Windows separators to POSIX separators for payload paths.
13963
- */
13964
- function toPosixPath(pathname) {
13965
- return pathname.split('\\').join('/');
13966
- }
13967
- /**
13968
- * Resolves writable filesystem directory used for artifact persistence.
13947
+ * Note: It always creates new Regex object
13948
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
13949
+ *
13950
+ * @private - TODO: [🧠] Maybe should be public?
13969
13951
  */
13970
- function resolveRunBrowserArtifactStorageDirectory() {
13971
- const configuredStorageDirectory = process.env[RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV];
13972
- if (configuredStorageDirectory && configuredStorageDirectory.trim()) {
13973
- return configuredStorageDirectory.trim();
13952
+ function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
13953
+ const allCommitments = [commitment, ...aliases];
13954
+ const patterns = allCommitments.map((commitment) => {
13955
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13956
+ return escapedCommitment.split(/\s+/).join('\\s+');
13957
+ });
13958
+ const keywordPattern = patterns.join('|');
13959
+ if (requiresContent) {
13960
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
13961
+ }
13962
+ else {
13963
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
13974
13964
  }
13975
- return DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY;
13976
- }
13977
- /**
13978
- * Resolves absolute filesystem path of one artifact filename.
13979
- */
13980
- function resolveRunBrowserArtifactFilesystemPath(artifactFilename) {
13981
- return join(resolveRunBrowserArtifactStorageDirectory(), artifactFilename);
13982
13965
  }
13983
13966
  /**
13984
- * Resolves payload path of one artifact filename used by replay renderers.
13967
+ * Generates a regex pattern to match a specific commitment type
13968
+ *
13969
+ * Note: It just matches the type part of the commitment
13970
+ * Note: It always creates new Regex object
13971
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
13972
+ *
13973
+ * @private
13985
13974
  */
13986
- function resolveRunBrowserArtifactPublicPath(artifactFilename) {
13987
- return toPosixPath(`${RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY}/${artifactFilename}`);
13975
+ function createCommitmentTypeRegex(commitment, aliases = []) {
13976
+ const allCommitments = [commitment, ...aliases];
13977
+ const patterns = allCommitments.map((commitment) => {
13978
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13979
+ return escapedCommitment.split(/\s+/).join('\\s+');
13980
+ });
13981
+ const keywordPattern = patterns.join('|');
13982
+ const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
13983
+ return regex;
13988
13984
  }
13989
13985
 
13990
13986
  /**
13991
- * Error code used for remote-browser infrastructure outages.
13992
- */
13993
- const REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE = 'REMOTE_BROWSER_UNAVAILABLE';
13994
- /**
13995
- * Error thrown when a remote Playwright browser cannot be reached.
13987
+ * Base implementation of CommitmentDefinition that provides common functionality
13988
+ * Most commitments can extend this class and only override the applyToAgentModelRequirements method
13989
+ *
13990
+ * @private
13996
13991
  */
13997
- class RemoteBrowserUnavailableError extends KnowledgeScrapeError {
13998
- constructor(options) {
13999
- var _a;
14000
- super(options.message);
14001
- this.code = REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
14002
- this.isRetryable = true;
14003
- this.debug = options.debug;
14004
- this.suggestedNextSteps =
14005
- (_a = options.suggestedNextSteps) !== null && _a !== void 0 ? _a : [
14006
- 'Verify remote browser infrastructure is running and reachable from Agents Server.',
14007
- 'Check firewall and DNS routing for the remote browser host and port.',
14008
- 'Retry later or continue with non-graphical fallback scraping.',
14009
- ];
14010
- this.cause = options.cause;
14011
- Object.setPrototypeOf(this, RemoteBrowserUnavailableError.prototype);
13992
+ class BaseCommitmentDefinition {
13993
+ constructor(type, aliases = []) {
13994
+ this.type = type;
13995
+ this.aliases = aliases;
14012
13996
  }
14013
- }
14014
- /**
14015
- * Returns true when an unknown value is one of the remote-browser outage errors.
14016
- */
14017
- function isRemoteBrowserUnavailableError(error) {
14018
- return error instanceof RemoteBrowserUnavailableError;
14019
- }
14020
- /**
14021
- * Sanitizes a remote websocket endpoint so debug payloads never expose path secrets.
14022
- */
14023
- function sanitizeRemoteBrowserEndpoint(wsEndpoint) {
14024
- var _a, _b;
14025
- try {
14026
- const parsedEndpoint = new URL(wsEndpoint);
14027
- return {
14028
- protocol: parsedEndpoint.protocol || null,
14029
- host: parsedEndpoint.hostname || null,
14030
- port: parsedEndpoint.port ? Number.parseInt(parsedEndpoint.port, 10) : null,
14031
- };
13997
+ /**
13998
+ * Whether this commitment requires content.
13999
+ * If true, regex will match only if there is content after the commitment keyword.
14000
+ * If false, regex will match even if there is no content.
14001
+ */
14002
+ get requiresContent() {
14003
+ return true;
14004
+ }
14005
+ /**
14006
+ * Optional UI/docs-only deprecation metadata.
14007
+ */
14008
+ get deprecation() {
14009
+ return undefined;
14010
+ }
14011
+ /**
14012
+ * Creates a regex pattern to match this commitment in agent source
14013
+ * Uses the existing createCommitmentRegex function as internal helper
14014
+ */
14015
+ createRegex() {
14016
+ return createCommitmentRegex(this.type, this.aliases, this.requiresContent);
14017
+ }
14018
+ /**
14019
+ * Creates a regex pattern to match just the commitment type
14020
+ * Uses the existing createCommitmentTypeRegex function as internal helper
14021
+ */
14022
+ createTypeRegex() {
14023
+ return createCommitmentTypeRegex(this.type, this.aliases);
14032
14024
  }
14033
- catch (_c) {
14034
- const hostPortMatch = wsEndpoint.trim().match(/^(?:wss?:\/\/)?(?<host>[^:/?#]+)(?::(?<port>\d{1,5}))?/i);
14035
- const host = ((_a = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _a === void 0 ? void 0 : _a.host) || null;
14036
- const parsedPort = (_b = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _b === void 0 ? void 0 : _b.port;
14025
+ /**
14026
+ * Helper method to create a new requirements object with updated system message
14027
+ * This is commonly used by many commitments
14028
+ */
14029
+ updateSystemMessage(requirements, messageUpdate) {
14030
+ const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
14037
14031
  return {
14038
- protocol: wsEndpoint.startsWith('wss://') ? 'wss:' : wsEndpoint.startsWith('ws://') ? 'ws:' : null,
14039
- host,
14040
- port: parsedPort ? Number.parseInt(parsedPort, 10) : null,
14032
+ ...requirements,
14033
+ systemMessage: newMessage,
14041
14034
  };
14042
14035
  }
14043
- }
14044
- /**
14045
- * Extracts network-like error code from unknown error payload.
14046
- */
14047
- function extractNetworkErrorCode(error) {
14048
- var _a;
14049
- if (error && typeof error === 'object') {
14050
- const maybeCode = error.code;
14051
- if (typeof maybeCode === 'string' && maybeCode.trim()) {
14052
- return maybeCode.trim().toUpperCase();
14053
- }
14054
- }
14055
- const message = getErrorMessage(error);
14056
- const match = message.match(/\b(ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNRESET|EHOSTUNREACH|ENETUNREACH)\b/i);
14057
- return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || null;
14058
- }
14059
- /**
14060
- * Classifies whether an unknown error most likely represents remote browser infra outage.
14061
- */
14062
- function isRemoteBrowserInfrastructureError(error) {
14063
- const networkErrorCode = extractNetworkErrorCode(error);
14064
- if (networkErrorCode) {
14065
- return true;
14036
+ /**
14037
+ * Helper method to append content to the system message
14038
+ */
14039
+ appendToSystemMessage(requirements, content, separator = '\n\n') {
14040
+ return this.updateSystemMessage(requirements, (currentMessage) => {
14041
+ if (!currentMessage.trim()) {
14042
+ return content;
14043
+ }
14044
+ return currentMessage + separator + content;
14045
+ });
14066
14046
  }
14067
- const message = getErrorMessage(error).toLowerCase();
14068
- const isWebSocketFailure = message.includes('websocket') ||
14069
- message.includes('<ws error>') ||
14070
- message.includes('ws connect error') ||
14071
- message.includes('socket hang up');
14072
- const hasHandshakeFailure = message.includes('unexpected server response') ||
14073
- message.includes('handshake') ||
14074
- message.includes('code=1006') ||
14075
- message.includes('disconnected');
14076
- return isWebSocketFailure && hasHandshakeFailure;
14077
- }
14078
- /**
14079
- * Converts unknown thrown values into safe string messages.
14080
- */
14081
- function getErrorMessage(error) {
14082
- return error instanceof Error ? error.message : String(error);
14083
- }
14084
- /**
14085
- * Converts unknown errors into stack payloads that are safe to render in debug mode.
14086
- */
14087
- function getErrorStack(error) {
14088
- return error instanceof Error && error.stack ? error.stack : null;
14089
- }
14090
-
14091
- /**
14092
- * Matches unsupported characters in snapshot file suffixes.
14093
- */
14094
- const SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN = /[^a-z0-9-]/g;
14095
- /**
14096
- * Creates one filesystem-safe optional filename suffix for a snapshot.
14097
- */
14098
- function createSnapshotFileSuffix(rawSuffix) {
14099
- if (!rawSuffix) {
14100
- return '';
14047
+ /**
14048
+ * Helper method to format one system-message section with an H2 heading.
14049
+ *
14050
+ * @param title - Section title without markdown prefix.
14051
+ * @param content - Section body content.
14052
+ * @returns Formatted section text using `##` heading.
14053
+ */
14054
+ createSystemMessageSection(title, content) {
14055
+ const normalizedTitle = title.trim().replace(/^#+\s*/, '');
14056
+ const normalizedContent = content.trim();
14057
+ return normalizedContent ? `## ${normalizedTitle}\n${normalizedContent}` : `## ${normalizedTitle}`;
14101
14058
  }
14102
- const normalized = rawSuffix
14103
- .trim()
14104
- .toLowerCase()
14105
- .replace(/\s+/g, '-')
14106
- .replace(SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN, '-')
14107
- .replace(/-+/g, '-')
14108
- .replace(/^-|-$/g, '');
14109
- return normalized;
14110
- }
14111
- /**
14112
- * Resolves snapshot filename for one session and optional stage suffix.
14113
- */
14114
- function resolveSnapshotFilename(sessionId, fileSuffix) {
14115
- const safeSuffix = createSnapshotFileSuffix(fileSuffix);
14116
- return safeSuffix ? `${sessionId}-${safeSuffix}.png` : `${sessionId}.png`;
14117
- }
14118
- /**
14119
- * Creates one user-facing description for an executed browser action.
14120
- */
14121
- function formatActionSummary(action) {
14122
- switch (action.type) {
14123
- case 'navigate':
14124
- return `Navigate to ${action.url}`;
14125
- case 'click':
14126
- return `Click ${action.selector}`;
14127
- case 'type':
14128
- return `Type into ${action.selector}`;
14129
- case 'wait':
14130
- return `Wait ${action.milliseconds}ms`;
14131
- case 'scroll':
14132
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
14059
+ /**
14060
+ * Helper method to create a new requirements object with updated prompt suffix
14061
+ */
14062
+ updatePromptSuffix(requirements, contentUpdate) {
14063
+ const newSuffix = typeof contentUpdate === 'string' ? contentUpdate : contentUpdate(requirements.promptSuffix);
14064
+ return {
14065
+ ...requirements,
14066
+ promptSuffix: newSuffix,
14067
+ };
14133
14068
  }
14134
- }
14135
- /**
14136
- * Screenshot/artifact and page-cleanup helpers for `run_browser`.
14137
- *
14138
- * @private function of `run_browser`
14139
- */
14140
- const runBrowserArtifacts = {
14141
14069
  /**
14142
- * Captures a screenshot artifact for the current page and returns relative path.
14070
+ * Helper method to append content to the prompt suffix
14071
+ * Default separator is a single newline for bullet lists.
14143
14072
  */
14144
- async captureSnapshot(page, sessionId, fileSuffix) {
14145
- const snapshotFilename = resolveSnapshotFilename(sessionId, fileSuffix);
14146
- const snapshotDirectoryPath = resolveRunBrowserArtifactStorageDirectory();
14147
- const snapshotPath = resolveRunBrowserArtifactFilesystemPath(snapshotFilename);
14148
- try {
14149
- await mkdir(snapshotDirectoryPath, { recursive: true });
14150
- try {
14151
- await page.screenshot({ path: snapshotPath, fullPage: true });
14152
- }
14153
- catch (error) {
14154
- console.warn('[run_browser] Full-page snapshot failed, retrying viewport-only screenshot', {
14155
- sessionId,
14156
- snapshotFilename,
14157
- error: getErrorMessage(error),
14158
- });
14159
- await page.screenshot({ path: snapshotPath, fullPage: false });
14073
+ appendToPromptSuffix(requirements, content, separator = '\n') {
14074
+ return this.updatePromptSuffix(requirements, (currentSuffix) => {
14075
+ if (!currentSuffix.trim()) {
14076
+ return content;
14160
14077
  }
14161
- return resolveRunBrowserArtifactPublicPath(snapshotFilename);
14162
- }
14163
- catch (error) {
14164
- console.error('[run_browser] Failed to capture snapshot', {
14165
- sessionId,
14166
- snapshotFilename,
14167
- error: getErrorMessage(error),
14168
- });
14169
- return null;
14170
- }
14171
- },
14078
+ return `${currentSuffix}${separator}${content}`;
14079
+ });
14080
+ }
14172
14081
  /**
14173
- * Safely retrieves page title from current browser page.
14082
+ * Helper method to add a comment section to the system message
14083
+ * Comments are lines starting with # that will be removed from the final system message
14084
+ * but can be useful for organizing and structuring the message during processing
14174
14085
  */
14175
- async getPageTitle(page) {
14176
- try {
14177
- return await page.title();
14086
+ addCommentSection(requirements, commentTitle, content, position = 'end') {
14087
+ const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
14088
+ if (position === 'beginning') {
14089
+ return this.updateSystemMessage(requirements, (currentMessage) => {
14090
+ if (!currentMessage.trim()) {
14091
+ return commentSection;
14092
+ }
14093
+ return commentSection + '\n\n' + currentMessage;
14094
+ });
14178
14095
  }
14179
- catch (_a) {
14180
- return null;
14096
+ else {
14097
+ return this.appendToSystemMessage(requirements, commentSection);
14181
14098
  }
14182
- },
14099
+ }
14183
14100
  /**
14184
- * Closes browser page and logs non-fatal cleanup errors.
14101
+ * Gets tool function implementations provided by this commitment
14102
+ *
14103
+ * When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
14185
14104
  */
14186
- async cleanupPage(page, sessionId) {
14187
- if (!page) {
14188
- return;
14189
- }
14190
- try {
14191
- await page.close();
14192
- }
14193
- catch (error) {
14194
- console.error('[run_browser] Failed to cleanup browser page', {
14195
- sessionId,
14196
- error: getErrorMessage(error),
14197
- });
14198
- }
14199
- },
14105
+ getToolFunctions() {
14106
+ return {};
14107
+ }
14200
14108
  /**
14201
- * Captures one screenshot artifact and enriches it with page metadata.
14109
+ * Gets human-readable titles for tool functions provided by this commitment
14110
+ *
14111
+ * This is used in the UI to show a user-friendly name instead of the technical function name.
14202
14112
  */
14203
- async captureSnapshotArtifact(options) {
14204
- const { page, sessionId, label, fileSuffix, actionIndex, action } = options;
14205
- const path = await this.captureSnapshot(page, sessionId, fileSuffix);
14206
- if (!path) {
14207
- return null;
14208
- }
14209
- const actionSummary = action ? formatActionSummary(action) : undefined;
14210
- return {
14211
- kind: 'screenshot',
14212
- label,
14213
- path,
14214
- capturedAt: new Date().toISOString(),
14215
- url: page.url(),
14216
- title: await this.getPageTitle(page),
14217
- actionIndex,
14218
- actionSummary,
14219
- };
14220
- },
14221
- };
14222
-
14223
- /**
14224
- * Shared constants used by the `run_browser` tool.
14225
- *
14226
- * @private internal constants of `run_browser`
14227
- */
14228
- const runBrowserConstants = {
14229
- sessionPrefix: 'agents-server-run-browser',
14230
- snapshotDirectory: '.playwright-cli',
14231
- resultSchema: 'promptbook/run-browser@1',
14232
- defaultWaitMs: 1000,
14233
- maxWaitMs: 60000,
14234
- defaultScrollPixels: 800,
14235
- defaultNavigationTimeoutMs: 20000,
14236
- defaultActionTimeoutMs: 15000,
14237
- fallbackDynamicContentWarning: 'Remote browser is unavailable. Fallback scraping was used and dynamic content may be missing.',
14238
- validationErrorCode: 'RUN_BROWSER_VALIDATION_ERROR',
14239
- navigationFailedErrorCode: 'RUN_BROWSER_NAVIGATION_FAILED',
14240
- actionFailedErrorCode: 'RUN_BROWSER_ACTION_FAILED',
14241
- cancelledErrorCode: 'RUN_BROWSER_CANCELLED',
14242
- unknownErrorCode: 'RUN_BROWSER_UNKNOWN_ERROR',
14243
- };
14113
+ getToolTitles() {
14114
+ return {};
14115
+ }
14116
+ }
14244
14117
 
14245
- const config = ConfigChecker.from({
14246
- ...process.env,
14247
- // Note: To expose env variables to the browser, using this seemingly strange syntax:
14248
- // @see https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser
14249
- NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
14250
- NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV,
14251
- NEXT_PUBLIC_VERCEL_TARGET_ENV: process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
14252
- NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
14253
- NEXT_PUBLIC_VERCEL_BRANCH_URL: process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL,
14254
- NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL: process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL,
14255
- NEXT_PUBLIC_VERCEL_GIT_PROVIDER: process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
14256
- NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
14257
- NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
14258
- NEXT_PUBLIC_VERCEL_GIT_REPO_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_ID,
14259
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
14260
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
14261
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
14262
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME,
14263
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
14264
- NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA,
14265
- NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
14266
- });
14267
14118
  /**
14268
- * Public URL of the deployment, e.g. "https://my-app.vercel.app"
14119
+ * ACTION commitment definition
14269
14120
  *
14270
- * Note: When a request resolves through the global `_Server` registry,
14271
- * this URL will be overridden by the matched server domain.
14272
- */
14273
- config.get('NEXT_PUBLIC_SITE_URL').url().value;
14274
- /**
14275
- * [♐️] Vercel environment: "development" | "preview" | "production"
14276
- */
14277
- config.get('NEXT_PUBLIC_VERCEL_ENV').value;
14278
- /**
14279
- * [♐️] Target environment – can be system or custom
14280
- */
14281
- config.get('NEXT_PUBLIC_VERCEL_TARGET_ENV').value;
14282
- /**
14283
- * [♐️] Deployment URL (without https://), e.g. "my-app-abc123.vercel.app"
14284
- */
14285
- config.get('NEXT_PUBLIC_VERCEL_URL').value;
14286
- /**
14287
- * [♐️] Branch URL (without https://), only for branch deployments
14288
- */
14289
- config.get('NEXT_PUBLIC_VERCEL_BRANCH_URL').value;
14290
- /**
14291
- * [♐️] Production domain of the project
14292
- */
14293
- config.get('NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL').value;
14294
- /**
14295
- * [♐️] Git provider (github | gitlab | bitbucket)
14296
- */
14297
- config.get('NEXT_PUBLIC_VERCEL_GIT_PROVIDER').value;
14298
- /**
14299
- * [♐️] Repository owner (e.g. "hejny")
14300
- */
14301
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER').value;
14302
- /**
14303
- * [♐️] Repository slug (e.g. "my-project")
14304
- */
14305
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG').value;
14306
- /**
14307
- * [♐️] Repository internal ID
14308
- */
14309
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_ID').value;
14310
- /**
14311
- * [♐️] Git commit SHA (short or long)
14312
- */
14313
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA').value;
14314
- /**
14315
- * [♐️] Commit message used for this deployment
14316
- */
14317
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE').value;
14318
- /**
14319
- * [♐️] Branch name (ref), e.g. "main"
14320
- */
14321
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF').value;
14322
- /**
14323
- * Author name of the commit
14324
- */
14325
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME').value;
14326
- /**
14327
- * [♐️] Author login/username
14328
- */
14329
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN').value;
14330
- /**
14331
- * [♐️] Previous deployment commit SHA (if exists)
14332
- */
14333
- config.get('NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA').value;
14334
- /**
14335
- * [♐️] Pull Request ID for PR-based deployments
14336
- */
14337
- config.get('NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID').value;
14338
- /**
14339
- * Supabase table prefix
14121
+ * The ACTION commitment defines specific actions or capabilities that the agent can perform.
14122
+ * This helps define what the agent is capable of doing and how it should approach tasks.
14340
14123
  *
14341
- * This remains the fallback/default prefix used before `_Server` contains records
14342
- * or for local development requests.
14343
- */
14344
- config.get('SUPABASE_TABLE_PREFIX').value;
14345
- /**
14346
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
14124
+ * Example usage in agent source:
14347
14125
  *
14348
- * When set, browser automation will connect to this remote server instead of launching a local browser.
14349
- * This is useful for environments like Vercel where running a full browser locally is not possible.
14350
- * Leave empty to use local browser mode.
14351
- */
14352
- const rawRemoteBrowserUrl = config.get('REMOTE_BROWSER_URL').value;
14353
- /**
14354
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
14126
+ * ```book
14127
+ * ACTION Can generate code snippets and explain programming concepts
14128
+ * ACTION Able to analyze data and provide insights
14129
+ * ```
14355
14130
  *
14356
- * When set, browser automation will connect to this remote server instead of launching a local browser.
14357
- * This is useful for environments like Vercel where running a full browser locally is not possible.
14358
- * Leave empty to use local browser mode.
14359
- */
14360
- const REMOTE_BROWSER_URL = typeof rawRemoteBrowserUrl === 'string' ? rawRemoteBrowserUrl : '';
14361
-
14362
- /**
14363
- * Reads a positive integer value from environment variables.
14131
+ * @private [🪔] Maybe export the commitments through some package
14364
14132
  */
14365
- function resolvePositiveIntFromEnv$1(variableName, defaultValue) {
14366
- const rawValue = process.env[variableName];
14367
- if (!rawValue || !rawValue.trim()) {
14368
- return defaultValue;
14369
- }
14370
- const parsed = Number.parseInt(rawValue.trim(), 10);
14371
- if (!Number.isFinite(parsed) || parsed <= 0) {
14372
- return defaultValue;
14133
+ class ActionCommitmentDefinition extends BaseCommitmentDefinition {
14134
+ constructor(type = 'ACTION') {
14135
+ super(type);
14373
14136
  }
14374
- return parsed;
14375
- }
14376
- /**
14377
- * Runtime helpers for mode/session/timeout handling in `run_browser`.
14378
- *
14379
- * @private function of `run_browser`
14380
- */
14381
- const runBrowserRuntime = {
14382
14137
  /**
14383
- * Creates a dedicated session id for one tool invocation.
14138
+ * Short one-line description of ACTION.
14384
14139
  */
14385
- createRunBrowserSessionId() {
14386
- return `${runBrowserConstants.sessionPrefix}-${randomUUID()}`;
14387
- },
14140
+ get description() {
14141
+ return 'Define agent capabilities and actions it can perform.';
14142
+ }
14388
14143
  /**
14389
- * Determines whether the browser tool is running in local or remote mode.
14144
+ * Icon for this commitment.
14390
14145
  */
14391
- resolveExecutionMode() {
14392
- return REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0 ? 'remote' : 'local';
14393
- },
14146
+ get icon() {
14147
+ return '';
14148
+ }
14394
14149
  /**
14395
- * Converts the execution mode into a human-readable label.
14396
- */
14397
- formatExecutionMode(mode) {
14398
- return mode === 'remote' ? 'remote-browser' : 'local-browser';
14399
- },
14400
- /**
14401
- * Resolves timeout configuration from env defaults and optional call overrides.
14402
- */
14403
- resolveTimeoutConfiguration(overrides) {
14404
- const envNavigationTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_NAVIGATION_TIMEOUT_MS', runBrowserConstants.defaultNavigationTimeoutMs);
14405
- const envActionTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_ACTION_TIMEOUT_MS', runBrowserConstants.defaultActionTimeoutMs);
14406
- const navigationTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.navigationMs) && Number.isFinite(overrides.navigationMs) && overrides.navigationMs > 0
14407
- ? Math.floor(overrides.navigationMs)
14408
- : envNavigationTimeoutMs;
14409
- const actionTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.actionMs) && Number.isFinite(overrides.actionMs) && overrides.actionMs > 0
14410
- ? Math.floor(overrides.actionMs)
14411
- : envActionTimeoutMs;
14412
- return {
14413
- navigationTimeoutMs,
14414
- actionTimeoutMs,
14415
- };
14416
- },
14417
- };
14418
-
14419
- /**
14420
- * Error classification and cancellation helpers used by `run_browser`.
14421
- *
14422
- * @private function of `run_browser`
14423
- */
14424
- const runBrowserErrorHandling = {
14425
- /**
14426
- * Creates one tagged ParseError used for deterministic input validation failures.
14427
- */
14428
- createRunBrowserValidationError(options) {
14429
- const error = new ParseError(options.message);
14430
- error.name = 'RunBrowserValidationError';
14431
- error.runBrowserCode = runBrowserConstants.validationErrorCode;
14432
- error.isRetryable = false;
14433
- error.suggestedNextSteps = [
14434
- 'Fix the action payload to match the run_browser schema.',
14435
- 'Check selectors and required action fields before retrying.',
14436
- ];
14437
- error.debug = options.debug;
14438
- return error;
14439
- },
14440
- /**
14441
- * Creates one tagged KnowledgeScrapeError used for navigation failures.
14442
- */
14443
- createRunBrowserNavigationError(options) {
14444
- const error = new KnowledgeScrapeError(options.message);
14445
- error.name = 'RunBrowserNavigationError';
14446
- error.runBrowserCode = runBrowserConstants.navigationFailedErrorCode;
14447
- error.isRetryable = false;
14448
- error.suggestedNextSteps = [
14449
- 'Verify the URL is reachable and not blocked.',
14450
- 'Retry with a simpler action sequence.',
14451
- ];
14452
- error.debug = options.debug;
14453
- error.cause = options.cause;
14454
- return error;
14455
- },
14456
- /**
14457
- * Creates one tagged KnowledgeScrapeError used for action failures.
14458
- */
14459
- createRunBrowserActionError(options) {
14460
- const error = new KnowledgeScrapeError(options.message);
14461
- error.name = 'RunBrowserActionError';
14462
- error.runBrowserCode = runBrowserConstants.actionFailedErrorCode;
14463
- error.isRetryable = false;
14464
- error.suggestedNextSteps = [
14465
- 'Verify selectors and action values.',
14466
- 'Reduce the action sequence to isolate the failing step.',
14467
- ];
14468
- error.debug = options.debug;
14469
- error.cause = options.cause;
14470
- return error;
14471
- },
14472
- /**
14473
- * Creates one tagged KnowledgeScrapeError used for cancellation.
14474
- */
14475
- createRunBrowserCancelledError(options) {
14476
- const error = new KnowledgeScrapeError(options.message);
14477
- error.name = 'RunBrowserCancelledError';
14478
- error.runBrowserCode = runBrowserConstants.cancelledErrorCode;
14479
- error.isRetryable = true;
14480
- error.suggestedNextSteps = [
14481
- 'Retry while request context is still active.',
14482
- 'Increase timeout if operation is expected to run longer.',
14483
- ];
14484
- error.debug = options.debug;
14485
- error.cause = options.cause;
14486
- return error;
14487
- },
14488
- /**
14489
- * Checks whether an unknown error carries run_browser classification tags.
14490
- */
14491
- isTaggedRunBrowserError(error) {
14492
- if (!error || typeof error !== 'object') {
14493
- return false;
14494
- }
14495
- const candidate = error;
14496
- return (typeof candidate.runBrowserCode === 'string' &&
14497
- typeof candidate.isRetryable === 'boolean' &&
14498
- Array.isArray(candidate.suggestedNextSteps) &&
14499
- typeof candidate.debug === 'object' &&
14500
- candidate.debug !== null);
14501
- },
14502
- /**
14503
- * Converts unknown errors into structured tool error payloads.
14504
- */
14505
- classifyRunBrowserToolError(options) {
14506
- if (isRemoteBrowserUnavailableError(options.error)) {
14507
- return {
14508
- code: options.error.code,
14509
- message: options.error.message,
14510
- isRetryable: options.error.isRetryable,
14511
- suggestedNextSteps: options.error.suggestedNextSteps,
14512
- debug: {
14513
- ...options.error.debug,
14514
- sessionId: options.sessionId,
14515
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
14516
- },
14517
- };
14518
- }
14519
- if (this.isTaggedRunBrowserError(options.error)) {
14520
- return {
14521
- code: options.error.runBrowserCode,
14522
- message: options.error.message,
14523
- isRetryable: options.error.isRetryable,
14524
- suggestedNextSteps: options.error.suggestedNextSteps,
14525
- debug: {
14526
- ...options.error.debug,
14527
- sessionId: options.sessionId,
14528
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
14529
- },
14530
- };
14531
- }
14532
- const remoteBrowserEndpoint = REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0
14533
- ? sanitizeRemoteBrowserEndpoint(REMOTE_BROWSER_URL.trim())
14534
- : null;
14535
- const message = getErrorMessage(options.error);
14536
- return {
14537
- code: runBrowserConstants.unknownErrorCode,
14538
- message,
14539
- isRetryable: false,
14540
- suggestedNextSteps: ['Inspect debug details to identify the failing phase.', 'Retry with fewer actions.'],
14541
- debug: {
14542
- sessionId: options.sessionId,
14543
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
14544
- remoteBrowserEndpoint,
14545
- message,
14546
- stack: getErrorStack(options.error),
14547
- },
14548
- };
14549
- },
14550
- /**
14551
- * Asserts that the run was not aborted.
14552
- */
14553
- assertNotAborted(signal, sessionId) {
14554
- if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
14555
- return;
14556
- }
14557
- throw this.createRunBrowserCancelledError({
14558
- message: 'run_browser execution was cancelled.',
14559
- debug: { sessionId },
14560
- });
14561
- },
14562
- /**
14563
- * Returns true when the tool error represents remote browser unavailability.
14564
- */
14565
- isRemoteBrowserUnavailableCode(code) {
14566
- return code === REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
14567
- },
14568
- };
14569
-
14570
- /**
14571
- * In-memory observability counters for browser tool execution.
14572
- */
14573
- const RUN_BROWSER_OBSERVABILITY = {
14574
- totalRuns: 0,
14575
- fallbackRuns: 0,
14576
- errorCodeCounts: {},
14577
- };
14578
- /**
14579
- * Observability counters and metric logging for `run_browser`.
14580
- *
14581
- * @private function of `run_browser`
14582
- */
14583
- const runBrowserObservability = {
14584
- /**
14585
- * Increments total-run counter and returns the updated value.
14586
- */
14587
- incrementTotalRuns() {
14588
- RUN_BROWSER_OBSERVABILITY.totalRuns++;
14589
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
14590
- },
14591
- /**
14592
- * Returns current total run count.
14593
- */
14594
- getTotalRuns() {
14595
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
14596
- },
14597
- /**
14598
- * Increments fallback counter and returns updated metrics.
14599
- */
14600
- incrementFallbackRunsAndGetMetrics() {
14601
- RUN_BROWSER_OBSERVABILITY.fallbackRuns++;
14602
- return {
14603
- fallbackRuns: RUN_BROWSER_OBSERVABILITY.fallbackRuns,
14604
- fallbackRate: RUN_BROWSER_OBSERVABILITY.totalRuns === 0
14605
- ? 0
14606
- : RUN_BROWSER_OBSERVABILITY.fallbackRuns / RUN_BROWSER_OBSERVABILITY.totalRuns,
14607
- };
14608
- },
14609
- /**
14610
- * Increments one error-code counter and returns the updated value.
14611
- */
14612
- incrementRunBrowserErrorCodeCounter(code) {
14613
- const currentValue = RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] || 0;
14614
- const nextValue = currentValue + 1;
14615
- RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] = nextValue;
14616
- return nextValue;
14617
- },
14618
- /**
14619
- * Writes one structured metric line for browser-tool observability.
14620
- */
14621
- logRunBrowserMetric(options) {
14622
- console.info('[run_browser][metric]', {
14623
- tool: 'run_browser',
14624
- mode: options.mode,
14625
- sessionId: options.sessionId,
14626
- event: options.event,
14627
- ...(options.payload || {}),
14628
- });
14629
- },
14630
- };
14631
-
14632
- /**
14633
- * Computes one compact preview of a fallback scrape payload.
14634
- */
14635
- function createContentPreview(content) {
14636
- const normalized = content.replace(/\s+/g, ' ').trim();
14637
- if (normalized.length <= 280) {
14638
- return normalized;
14639
- }
14640
- return `${normalized.slice(0, 277)}...`;
14641
- }
14642
- /**
14643
- * Payload and markdown formatters for `run_browser` outcomes.
14644
- *
14645
- * @private function of `run_browser`
14646
- */
14647
- const runBrowserResultFormatting = {
14648
- /**
14649
- * Produces one structured payload consumed by chat UI browser replay renderers.
14650
- */
14651
- createResultPayload(options) {
14652
- return {
14653
- schema: runBrowserConstants.resultSchema,
14654
- sessionId: options.sessionId,
14655
- mode: options.mode,
14656
- modeUsed: options.modeUsed,
14657
- initialUrl: options.initialUrl,
14658
- finalUrl: options.finalUrl,
14659
- finalTitle: options.finalTitle,
14660
- executedActions: options.executedActions,
14661
- artifacts: options.artifacts,
14662
- warning: options.warning,
14663
- error: options.error,
14664
- fallback: options.modeUsed === 'fallback' && options.fallbackContent !== null
14665
- ? {
14666
- scraper: 'fetch_url_content',
14667
- contentPreview: createContentPreview(options.fallbackContent),
14668
- }
14669
- : null,
14670
- timing: options.timing,
14671
- };
14672
- },
14673
- /**
14674
- * Produces a model-friendly markdown summary from browser execution artifacts.
14675
- */
14676
- formatSuccessResult(options) {
14677
- const { payload, snapshotPath } = options;
14678
- return spaceTrim$1((block) => {
14679
- var _a, _b, _c;
14680
- return `
14681
- # Browser run completed
14682
-
14683
- **Session:** ${payload.sessionId}
14684
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
14685
- **Mode used:** ${payload.modeUsed}
14686
- **Initial URL:** ${payload.initialUrl}
14687
- **Executed actions:** ${payload.executedActions.length}
14688
-
14689
- ## Final page
14690
-
14691
- - URL: ${payload.finalUrl || 'Unknown'}
14692
- - Title: ${payload.finalTitle || 'Unknown'}
14693
-
14694
- ## Timings
14695
-
14696
- - Connect: ${(_a = payload.timing.connectDurationMs) !== null && _a !== void 0 ? _a : 'Unknown'} ms
14697
- - Initial navigation: ${(_b = payload.timing.initialNavigationDurationMs) !== null && _b !== void 0 ? _b : 'Unknown'} ms
14698
- - Time to first byte: ${(_c = payload.timing.timeToFirstByteMs) !== null && _c !== void 0 ? _c : 'Unknown'} ms
14699
- - Total: ${payload.timing.totalDurationMs} ms
14700
-
14701
- ${payload.artifacts.length === 0
14702
- ? ''
14703
- : `
14704
- ## Visual replay
14705
-
14706
- ${payload.artifacts
14707
- .map((artifact, index) => {
14708
- const actionPart = artifact.actionSummary ? ` (${artifact.actionSummary})` : '';
14709
- return `- ${index + 1}. ${artifact.label}${actionPart}: ${artifact.path}`;
14710
- })
14711
- .join('\n')}
14712
- `}
14713
-
14714
- ${!snapshotPath
14715
- ? ''
14716
- : `
14717
- ## Final snapshot
14718
-
14719
- ${snapshotPath}
14720
- `}
14721
-
14722
- ## Playback payload
14723
-
14724
- \`\`\`json
14725
- ${JSON.stringify(payload, null, 2)}
14726
- \`\`\`
14727
-
14728
- ${block(payload.executedActions.length === 0
14729
- ? ''
14730
- : `
14731
- ## Action log
14732
-
14733
- ${payload.executedActions
14734
- .map((action, index) => `- ${index + 1}. ${JSON.stringify(action)}`)
14735
- .join('\n')}
14736
- `)}
14737
-
14738
- Note: Browser page has been automatically closed to free up resources.
14739
- `;
14740
- });
14741
- },
14742
- /**
14743
- * Produces a model-friendly markdown payload when fallback scraping is used.
14744
- */
14745
- formatFallbackResult(options) {
14746
- const { payload, fallbackContent, requestedActions } = options;
14747
- return spaceTrim$1(`
14748
- # Browser run completed with fallback
14749
-
14750
- **Session:** ${payload.sessionId}
14751
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
14752
- **Mode used:** ${payload.modeUsed}
14753
- **Initial URL:** ${payload.initialUrl}
14754
- **Requested actions:** ${requestedActions}
14755
- **Executed actions:** ${payload.executedActions.length}
14756
- **Warning:** ${payload.warning || runBrowserConstants.fallbackDynamicContentWarning}
14757
-
14758
- ## Extracted content
14759
-
14760
- ${fallbackContent}
14761
-
14762
- ## Playback payload
14763
-
14764
- \`\`\`json
14765
- ${JSON.stringify(payload, null, 2)}
14766
- \`\`\`
14767
- `);
14768
- },
14769
- /**
14770
- * Produces a model-friendly markdown error payload from browser execution failures.
14771
- */
14772
- formatErrorResult(options) {
14773
- const { payload } = options;
14774
- const toolError = payload.error;
14775
- const suggestedNextSteps = (toolError === null || toolError === void 0 ? void 0 : toolError.suggestedNextSteps) || [];
14776
- return spaceTrim$1(`
14777
- # Browser run failed
14778
-
14779
- **Session:** ${payload.sessionId}
14780
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
14781
- **Mode used:** ${payload.modeUsed}
14782
- **Initial URL:** ${payload.initialUrl}
14783
- **Error code:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.code) || runBrowserConstants.unknownErrorCode}
14784
- **Error:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.message) || 'Unknown browser tool error'}
14785
-
14786
- ${suggestedNextSteps.length === 0
14787
- ? ''
14788
- : `
14789
- ## Suggested next steps
14790
-
14791
- ${suggestedNextSteps.map((step) => `- ${step}`).join('\n')}
14792
- `}
14793
-
14794
- ## Playback payload
14795
-
14796
- \`\`\`json
14797
- ${JSON.stringify(payload, null, 2)}
14798
- \`\`\`
14799
-
14800
- The browser tool could not complete the requested actions.
14801
- `);
14802
- },
14803
- };
14804
-
14805
- /**
14806
- * Normalize options for `execCommand` and `execCommands`
14807
- *
14808
- * Note: `$` is used to indicate that this function behaves differently according to `process.platform`
14809
- *
14810
- * @private internal utility of `execCommand` and `execCommands`
14811
- */
14812
- function $execCommandNormalizeOptions(options) {
14813
- var _a, _b, _c, _d;
14814
- let command;
14815
- let cwd;
14816
- let crashOnError;
14817
- let args = [];
14818
- let timeout;
14819
- let isVerbose;
14820
- let env;
14821
- if (typeof options === 'string') {
14822
- // TODO: [1] DRY default values
14823
- command = options;
14824
- cwd = process.cwd();
14825
- crashOnError = true;
14826
- timeout = Infinity; // <- TODO: [⏳]
14827
- isVerbose = DEFAULT_IS_VERBOSE;
14828
- env = undefined;
14829
- }
14830
- else {
14831
- /*
14832
- TODO:
14833
- if ((options as any).commands !== undefined) {
14834
- commands = (options as any).commands;
14835
- } else {
14836
- commands = [(options as any).command];
14837
- }
14838
- */
14839
- // TODO: [1] DRY default values
14840
- command = options.command;
14841
- cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
14842
- crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
14843
- timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
14844
- isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
14845
- env = options.env;
14846
- }
14847
- // TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
14848
- const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
14849
- .map(([match]) => match)
14850
- .filter((arg) => arg !== '');
14851
- if (_.length > 1) {
14852
- [command, ...args] = _;
14853
- }
14854
- if (options.args) {
14855
- args = [...args, ...options.args];
14856
- }
14857
- let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
14858
- if (['ts-node'].includes(humanReadableCommand)) {
14859
- humanReadableCommand += ` ${args[1]}`;
14860
- }
14861
- if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
14862
- command = `${command}.cmd`;
14863
- }
14864
- return { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose, env };
14865
- }
14866
- // TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
14867
-
14868
- /**
14869
- * Run one command in a shell
14870
- *
14871
- *
14872
- * Note: There are 2 similar functions in the codebase:
14873
- * - `$execCommand` which runs a single command
14874
- * - `$execCommands` which runs multiple commands
14875
- * Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
14876
- *
14877
- * @public exported from `@promptbook/node`
14878
- */
14879
- function $execCommand(options) {
14880
- if (!$isRunningInNode()) {
14881
- throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
14882
- }
14883
- return new Promise((resolve, reject) => {
14884
- // eslint-disable-next-line prefer-const
14885
- const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
14886
- if (timeout !== Infinity) {
14887
- // TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
14888
- forTime(timeout).then(() => {
14889
- if (crashOnError) {
14890
- reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
14891
- }
14892
- else {
14893
- console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
14894
- // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
14895
- resolve('Command exceeded time limit');
14896
- }
14897
- });
14898
- }
14899
- if (isVerbose) {
14900
- console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
14901
- }
14902
- try {
14903
- const commandProcess = spawn(command, args, {
14904
- cwd,
14905
- shell: true,
14906
- env: env ? { ...process.env, ...env } : process.env,
14907
- });
14908
- if (isVerbose) {
14909
- commandProcess.on('message', (message) => {
14910
- console.info({ message });
14911
- });
14912
- }
14913
- const output = [];
14914
- commandProcess.stdout.on('data', (stdout) => {
14915
- output.push(stdout.toString());
14916
- if (isVerbose) {
14917
- console.info(stdout.toString());
14918
- }
14919
- });
14920
- commandProcess.stderr.on('data', (stderr) => {
14921
- output.push(stderr.toString());
14922
- if (isVerbose && stderr.toString().trim()) {
14923
- console.warn(stderr.toString());
14924
- // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
14925
- }
14926
- });
14927
- const finishWithCode = (code) => {
14928
- if (code !== 0) {
14929
- if (crashOnError) {
14930
- reject(new Error(output.join('\n').trim() ||
14931
- `Command "${humanReadableCommand}" exited with code ${code}`));
14932
- }
14933
- else {
14934
- if (isVerbose) {
14935
- console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
14936
- // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
14937
- }
14938
- resolve(spaceTrim$1(output.join('\n')));
14939
- }
14940
- }
14941
- else {
14942
- resolve(spaceTrim$1(output.join('\n')));
14943
- }
14944
- };
14945
- commandProcess.on('close', finishWithCode);
14946
- commandProcess.on('exit', finishWithCode);
14947
- commandProcess.on('disconnect', () => {
14948
- // Note: Unexpected disconnection should always result in rejection
14949
- reject(new Error(`Command "${humanReadableCommand}" disconnected`));
14950
- });
14951
- commandProcess.on('error', (error) => {
14952
- if (crashOnError) {
14953
- reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
14954
- }
14955
- else {
14956
- if (isVerbose) {
14957
- console.warn(error);
14958
- // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
14959
- }
14960
- resolve(spaceTrim$1(output.join('\n')));
14961
- }
14962
- });
14963
- }
14964
- catch (error) {
14965
- // Note: Unexpected error in sync code should always result in rejection
14966
- reject(error);
14967
- }
14968
- });
14969
- }
14970
- /**
14971
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
14972
- */
14973
-
14974
- /**
14975
- * Attempts to locate the specified application on a Linux system using the 'which' command.
14976
- * Returns the path to the executable if found, or null otherwise.
14977
- *
14978
- * @private within the repository
14979
- */
14980
- async function locateAppOnLinux({ linuxWhich, }) {
14981
- try {
14982
- const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
14983
- return result.trim();
14984
- }
14985
- catch (error) {
14986
- assertsError(error);
14987
- return null;
14988
- }
14989
- }
14990
- /**
14991
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
14992
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
14993
- */
14994
-
14995
- /**
14996
- * Checks if the file is executable
14997
- *
14998
- * @private within the repository
14999
- */
15000
- async function isExecutable(path, fs) {
15001
- try {
15002
- await fs.access(path, fs.constants.X_OK);
15003
- return true;
15004
- }
15005
- catch (error) {
15006
- return false;
15007
- }
15008
- }
15009
- /**
15010
- * Note: Not [~🟢~] because it is not directly dependent on `fs
15011
- * TODO: [🖇] What about symlinks?
15012
- */
15013
-
15014
- // Note: Module `userhome` has no types available, so it is imported using `require`
15015
- // @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
15016
- // eslint-disable-next-line @typescript-eslint/no-var-requires
15017
- const userhome = require('userhome');
15018
- /**
15019
- * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
15020
- * Returns the path to the executable if found, or null otherwise.
15021
- *
15022
- * @private within the repository
15023
- */
15024
- async function locateAppOnMacOs({ macOsName, }) {
15025
- try {
15026
- const toExec = `/Contents/MacOS/${macOsName}`;
15027
- const regPath = `/Applications/${macOsName}.app` + toExec;
15028
- const altPath = userhome(regPath.slice(1));
15029
- if (await isExecutable(regPath, $provideFilesystemForNode())) {
15030
- return regPath;
15031
- }
15032
- else if (await isExecutable(altPath, $provideFilesystemForNode())) {
15033
- return altPath;
15034
- }
15035
- const result = await $execCommand({
15036
- crashOnError: true,
15037
- command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
15038
- });
15039
- return result.trim() + toExec;
15040
- }
15041
- catch (error) {
15042
- assertsError(error);
15043
- return null;
15044
- }
15045
- }
15046
- /**
15047
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
15048
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
15049
- */
15050
-
15051
- /**
15052
- * Attempts to locate the specified application on a Windows system by searching common installation directories.
15053
- * Returns the path to the executable if found, or null otherwise.
15054
- *
15055
- * @private within the repository
15056
- */
15057
- async function locateAppOnWindows({ appName, windowsSuffix, }) {
15058
- try {
15059
- const prefixes = [
15060
- process.env.LOCALAPPDATA,
15061
- join(process.env.LOCALAPPDATA || '', 'Programs'),
15062
- process.env.PROGRAMFILES,
15063
- process.env['PROGRAMFILES(X86)'],
15064
- ];
15065
- for (const prefix of prefixes) {
15066
- const path = prefix + windowsSuffix;
15067
- if (await isExecutable(path, $provideFilesystemForNode())) {
15068
- return path;
15069
- }
15070
- }
15071
- throw new Error(`Can not locate app ${appName} on Windows.`);
15072
- }
15073
- catch (error) {
15074
- assertsError(error);
15075
- return null;
15076
- }
15077
- }
15078
- /**
15079
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
15080
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
15081
- */
15082
-
15083
- /**
15084
- * Locates an application on the system
15085
- *
15086
- * @private within the repository
15087
- */
15088
- function locateApp(options) {
15089
- if (!$isRunningInNode()) {
15090
- throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
15091
- }
15092
- const { appName, linuxWhich, windowsSuffix, macOsName } = options;
15093
- if (process.platform === 'win32') {
15094
- if (windowsSuffix) {
15095
- return locateAppOnWindows({ appName, windowsSuffix });
15096
- }
15097
- else {
15098
- throw new Error(`${appName} is not available on Windows.`);
15099
- }
15100
- }
15101
- else if (process.platform === 'darwin') {
15102
- if (macOsName) {
15103
- return locateAppOnMacOs({ macOsName });
15104
- }
15105
- else {
15106
- throw new Error(`${appName} is not available on macOS.`);
15107
- }
15108
- }
15109
- else {
15110
- if (linuxWhich) {
15111
- return locateAppOnLinux({ linuxWhich });
15112
- }
15113
- else {
15114
- throw new Error(`${appName} is not available on Linux.`);
15115
- }
15116
- }
15117
- }
15118
- /**
15119
- * TODO: [🧠][♿] Maybe export through `@promptbook/node`
15120
- * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
15121
- */
15122
-
15123
- /**
15124
- * @@@
15125
- *
15126
- * @private within the repository
15127
- */
15128
- function locateChrome() {
15129
- return locateApp({
15130
- appName: 'Chrome',
15131
- linuxWhich: 'google-chrome',
15132
- windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
15133
- macOsName: 'Google Chrome',
15134
- });
15135
- }
15136
-
15137
- /**
15138
- * Creates one standard abort error for cancelled retry loops.
15139
- *
15140
- * @private utility for Agents Server runtime retries
15141
- */
15142
- function createAbortError$1() {
15143
- const error = new Error('Operation was aborted.');
15144
- error.name = 'AbortError';
15145
- return error;
15146
- }
15147
- /**
15148
- * Throws when the supplied signal is already aborted.
15149
- *
15150
- * @private utility for Agents Server runtime retries
15151
- */
15152
- function assertNotAborted(signal) {
15153
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
15154
- throw createAbortError$1();
15155
- }
15156
- }
15157
- /**
15158
- * Waits for a duration while respecting cancellation.
15159
- *
15160
- * @private utility for Agents Server runtime retries
15161
- */
15162
- async function sleepWithAbort(delayMs, signal) {
15163
- if (delayMs <= 0) {
15164
- assertNotAborted(signal);
15165
- return;
15166
- }
15167
- await new Promise((resolve, reject) => {
15168
- const timeout = setTimeout(() => {
15169
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
15170
- resolve();
15171
- }, delayMs);
15172
- const onAbort = () => {
15173
- clearTimeout(timeout);
15174
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
15175
- reject(createAbortError$1());
15176
- };
15177
- signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
15178
- });
15179
- }
15180
- /**
15181
- * Resolves the retry wait duration for one failed attempt.
15182
- *
15183
- * @private utility for Agents Server runtime retries
15184
- */
15185
- function resolveBackoffDelayMs(options) {
15186
- const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
15187
- const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
15188
- const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
15189
- return Math.max(0, Math.round(boundedDelay + jitterDelay));
15190
- }
15191
- /**
15192
- * Retries one async operation with exponential backoff and jitter.
15193
- *
15194
- * @private utility for Agents Server runtime retries
15195
- */
15196
- async function retryWithBackoff(operation, options) {
15197
- var _a, _b, _c;
15198
- const startedAt = Date.now();
15199
- const totalAttempts = Math.max(1, options.retries + 1);
15200
- const random = (_a = options.random) !== null && _a !== void 0 ? _a : Math.random;
15201
- const sleep = (_b = options.sleep) !== null && _b !== void 0 ? _b : sleepWithAbort;
15202
- for (let attempt = 1; attempt <= totalAttempts; attempt++) {
15203
- assertNotAborted(options.signal);
15204
- try {
15205
- const value = await operation(attempt);
15206
- return {
15207
- value,
15208
- attempts: attempt,
15209
- durationMs: Date.now() - startedAt,
15210
- };
15211
- }
15212
- catch (error) {
15213
- const isLastAttempt = attempt >= totalAttempts;
15214
- const isRetryable = options.shouldRetry ? options.shouldRetry(error, attempt) : true;
15215
- if (isLastAttempt || !isRetryable) {
15216
- throw error;
15217
- }
15218
- const delayMs = resolveBackoffDelayMs({
15219
- attempt,
15220
- initialDelayMs: options.initialDelayMs,
15221
- maxDelayMs: options.maxDelayMs,
15222
- backoffFactor: options.backoffFactor,
15223
- jitterRatio: options.jitterRatio,
15224
- random,
15225
- });
15226
- (_c = options.onRetry) === null || _c === void 0 ? void 0 : _c.call(options, {
15227
- attempt,
15228
- retries: options.retries,
15229
- delayMs,
15230
- error,
15231
- });
15232
- await sleep(delayMs, options.signal);
15233
- }
15234
- }
15235
- throw new Error('Retry loop exited unexpectedly.');
15236
- }
15237
-
15238
- const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
15239
- /**
15240
- * Default remote browser connect timeout in milliseconds.
15241
- */
15242
- const DEFAULT_REMOTE_CONNECT_TIMEOUT_MS = 10000;
15243
- /**
15244
- * Default retry count for remote browser connection establishment.
15245
- */
15246
- const DEFAULT_REMOTE_CONNECT_RETRIES = 2;
15247
- /**
15248
- * Default initial retry delay for remote browser connection.
15249
- */
15250
- const DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS = 250;
15251
- /**
15252
- * Default maximum retry delay for remote browser connection.
15253
- */
15254
- const DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS = 1000;
15255
- /**
15256
- * Default exponential multiplier for remote browser retry delay.
15257
- */
15258
- const DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR = 4;
15259
- /**
15260
- * Default retry jitter ratio for remote browser connection.
15261
- */
15262
- const DEFAULT_REMOTE_CONNECT_JITTER_RATIO = 0.2;
15263
- /**
15264
- * In-memory metrics counters for remote browser connect attempts.
15265
- */
15266
- const REMOTE_BROWSER_CONNECT_METRICS = {
15267
- success: 0,
15268
- failure: 0,
15269
- };
15270
- /**
15271
- * Reads a positive integer from environment variables with a fallback default.
15272
- */
15273
- function resolvePositiveIntFromEnv(variableName, defaultValue) {
15274
- const rawValue = process.env[variableName];
15275
- if (!rawValue || !rawValue.trim()) {
15276
- return defaultValue;
15277
- }
15278
- const parsed = Number.parseInt(rawValue.trim(), 10);
15279
- if (!Number.isFinite(parsed) || parsed <= 0) {
15280
- return defaultValue;
15281
- }
15282
- return parsed;
15283
- }
15284
- /**
15285
- * Reads a positive number from environment variables with a fallback default.
15286
- */
15287
- function resolvePositiveNumberFromEnv(variableName, defaultValue) {
15288
- const rawValue = process.env[variableName];
15289
- if (!rawValue || !rawValue.trim()) {
15290
- return defaultValue;
15291
- }
15292
- const parsed = Number.parseFloat(rawValue.trim());
15293
- if (!Number.isFinite(parsed) || parsed <= 0) {
15294
- return defaultValue;
15295
- }
15296
- return parsed;
15297
- }
15298
- /**
15299
- * Reads a non-negative integer from environment variables with a fallback default.
15300
- */
15301
- function resolveNonNegativeIntFromEnv(variableName, defaultValue) {
15302
- const rawValue = process.env[variableName];
15303
- if (!rawValue || !rawValue.trim()) {
15304
- return defaultValue;
15305
- }
15306
- const parsed = Number.parseInt(rawValue.trim(), 10);
15307
- if (!Number.isFinite(parsed) || parsed < 0) {
15308
- return defaultValue;
15309
- }
15310
- return parsed;
15311
- }
15312
- /**
15313
- * Reads a non-negative number from environment variables with a fallback default.
15314
- */
15315
- function resolveNonNegativeNumberFromEnv(variableName, defaultValue) {
15316
- const rawValue = process.env[variableName];
15317
- if (!rawValue || !rawValue.trim()) {
15318
- return defaultValue;
15319
- }
15320
- const parsed = Number.parseFloat(rawValue.trim());
15321
- if (!Number.isFinite(parsed) || parsed < 0) {
15322
- return defaultValue;
15323
- }
15324
- return parsed;
15325
- }
15326
- /**
15327
- * Creates one standard abort error.
15328
- */
15329
- function createAbortError() {
15330
- const error = new Error('Browser connection request was aborted.');
15331
- error.name = 'AbortError';
15332
- return error;
15333
- }
15334
- /**
15335
- * Provides browser context instances with support for both local and remote browser connections.
15336
- *
15337
- * This provider manages browser lifecycle and supports:
15338
- * - Local mode: Launches a persistent Chromium context on the same machine
15339
- * - Remote mode: Connects to a remote Playwright browser via WebSocket
15340
- *
15341
- * The remote mode is useful for environments like Vercel where running a full browser
15342
- * is not possible due to resource constraints.
15343
- *
15344
- * @private internal utility for Agents Server browser tools
15345
- */
15346
- class BrowserConnectionProvider {
15347
- /**
15348
- * Creates a new BrowserConnectionProvider.
15349
- *
15350
- * @param options - Provider options
15351
- * @param options.isVerbose - Enable verbose logging
15352
- */
15353
- constructor(options = {}) {
15354
- var _a, _b, _c, _d, _e, _f, _g, _h;
15355
- this.browserContext = null;
15356
- this.connectionMode = null;
15357
- this.isVerbose = (_a = options.isVerbose) !== null && _a !== void 0 ? _a : false;
15358
- this.remoteConnectTimeoutMs =
15359
- (_b = options.remoteConnectTimeoutMs) !== null && _b !== void 0 ? _b : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_TIMEOUT_MS', DEFAULT_REMOTE_CONNECT_TIMEOUT_MS);
15360
- this.remoteConnectRetries =
15361
- (_c = options.remoteConnectRetries) !== null && _c !== void 0 ? _c : resolveNonNegativeIntFromEnv('RUN_BROWSER_CONNECT_RETRIES', DEFAULT_REMOTE_CONNECT_RETRIES);
15362
- this.remoteConnectBackoffInitialMs =
15363
- (_d = options.remoteConnectBackoffInitialMs) !== null && _d !== void 0 ? _d : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_INITIAL_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS);
15364
- this.remoteConnectBackoffMaxMs =
15365
- (_e = options.remoteConnectBackoffMaxMs) !== null && _e !== void 0 ? _e : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_MAX_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS);
15366
- this.remoteConnectBackoffFactor =
15367
- (_f = options.remoteConnectBackoffFactor) !== null && _f !== void 0 ? _f : resolvePositiveNumberFromEnv('RUN_BROWSER_CONNECT_BACKOFF_FACTOR', DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR);
15368
- this.remoteConnectJitterRatio =
15369
- (_g = options.remoteConnectJitterRatio) !== null && _g !== void 0 ? _g : resolveNonNegativeNumberFromEnv('RUN_BROWSER_CONNECT_JITTER_RATIO', DEFAULT_REMOTE_CONNECT_JITTER_RATIO);
15370
- this.random = (_h = options.random) !== null && _h !== void 0 ? _h : Math.random;
15371
- this.sleep = options.sleep;
15372
- }
15373
- /**
15374
- * Gets a browser context, creating a new one if needed.
15375
- *
15376
- * This method automatically determines whether to use local or remote browser
15377
- * based on the REMOTE_BROWSER_URL environment variable.
15378
- *
15379
- * @returns Browser context instance
15380
- */
15381
- async getBrowserContext(options = {}) {
15382
- var _a;
15383
- if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
15384
- throw createAbortError();
15385
- }
15386
- // Check if we have a cached connection that's still valid
15387
- if (this.browserContext !== null && this.isBrowserContextAlive(this.browserContext)) {
15388
- return this.browserContext;
15389
- }
15390
- // Determine connection mode from configuration
15391
- const mode = this.resolveConnectionMode();
15392
- this.connectionMode = mode;
15393
- if (this.isVerbose) {
15394
- console.info('[BrowserConnectionProvider] Creating new browser context', {
15395
- mode: mode.type,
15396
- wsEndpoint: mode.type === 'remote' ? mode.wsEndpoint : undefined,
15397
- });
15398
- }
15399
- // Create new browser context based on mode
15400
- if (mode.type === 'local') {
15401
- this.browserContext = await this.createLocalBrowserContext();
15402
- }
15403
- else {
15404
- this.browserContext = await this.createRemoteBrowserContext(mode.wsEndpoint, options);
15405
- }
15406
- return this.browserContext;
15407
- }
15408
- /**
15409
- * Closes all pages in the current browser context.
15410
- *
15411
- * This method is useful for cleanup between agent tasks without closing
15412
- * the entire browser instance.
15413
- */
15414
- async closeAllPages() {
15415
- if (!this.browserContext) {
15416
- return;
15417
- }
15418
- try {
15419
- const pages = this.browserContext.pages();
15420
- if (this.isVerbose) {
15421
- console.info('[BrowserConnectionProvider] Closing all pages', {
15422
- pageCount: pages.length,
15423
- });
15424
- }
15425
- await Promise.all(pages.map((page) => page.close().catch((error) => {
15426
- console.error('[BrowserConnectionProvider] Failed to close page', { error });
15427
- })));
15428
- }
15429
- catch (error) {
15430
- console.error('[BrowserConnectionProvider] Error closing pages', { error });
15431
- }
15432
- }
15433
- /**
15434
- * Closes the browser context and disconnects from the browser.
15435
- *
15436
- * This should be called when the browser is no longer needed to free up resources.
15437
- * For local mode, this closes the browser process. For remote mode, it disconnects
15438
- * from the remote browser but doesn't shut down the remote server.
15439
- */
15440
- async close() {
15441
- var _a;
15442
- if (!this.browserContext) {
15443
- return;
15444
- }
15445
- try {
15446
- if (this.isVerbose) {
15447
- console.info('[BrowserConnectionProvider] Closing browser context', {
15448
- mode: (_a = this.connectionMode) === null || _a === void 0 ? void 0 : _a.type,
15449
- });
15450
- }
15451
- await this.browserContext.close();
15452
- this.browserContext = null;
15453
- this.connectionMode = null;
15454
- }
15455
- catch (error) {
15456
- console.error('[BrowserConnectionProvider] Error closing browser context', { error });
15457
- // Reset state even if close fails
15458
- this.browserContext = null;
15459
- this.connectionMode = null;
15460
- }
15461
- }
15462
- /**
15463
- * Checks if a browser context is still alive and connected.
15464
- *
15465
- * @param context - Browser context to check
15466
- * @returns True if the context is connected and usable
15467
- */
15468
- isBrowserContextAlive(context) {
15469
- try {
15470
- const browser = context.browser();
15471
- return browser !== null && browser.isConnected();
15472
- }
15473
- catch (_a) {
15474
- return false;
15475
- }
15476
- }
15477
- /**
15478
- * Determines whether to use local or remote browser based on configuration.
15479
- *
15480
- * @returns Connection mode configuration
15481
- */
15482
- resolveConnectionMode() {
15483
- const remoteBrowserUrl = REMOTE_BROWSER_URL;
15484
- if (remoteBrowserUrl && remoteBrowserUrl.trim().length > 0) {
15485
- return {
15486
- type: 'remote',
15487
- wsEndpoint: remoteBrowserUrl.trim(),
15488
- };
15489
- }
15490
- return { type: 'local' };
15491
- }
15492
- /**
15493
- * Creates a local browser context using persistent Chromium.
15494
- *
15495
- * @returns Local browser context
15496
- */
15497
- async createLocalBrowserContext() {
15498
- if (this.isVerbose) {
15499
- console.info('[BrowserConnectionProvider] Launching local browser context');
15500
- }
15501
- const userDataDir = join(DEFAULT_BROWSER_USER_DATA_DIR, 'run-browser');
15502
- await mkdir(userDataDir, { recursive: true });
15503
- const launchOptions = {
15504
- headless: false,
15505
- args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
15506
- };
15507
- try {
15508
- const chromePath = await locateChrome();
15509
- launchOptions.executablePath = chromePath;
15510
- }
15511
- catch (error) {
15512
- if (this.isVerbose) {
15513
- console.warn('[BrowserConnectionProvider] Could not locate system Chrome; using Playwright bundled Chromium', {
15514
- error: error instanceof Error ? error.message : String(error),
15515
- });
15516
- }
15517
- }
15518
- return await chromium.launchPersistentContext(userDataDir, launchOptions);
15519
- }
15520
- /**
15521
- * Creates a remote browser context by connecting to a Playwright server.
15522
- *
15523
- * @param wsEndpoint - WebSocket endpoint of the remote Playwright server
15524
- * @returns Remote browser context
15525
- */
15526
- async createRemoteBrowserContext(wsEndpoint, options) {
15527
- const endpointDebug = sanitizeRemoteBrowserEndpoint(wsEndpoint);
15528
- const startedAt = Date.now();
15529
- if (this.isVerbose) {
15530
- console.info('[BrowserConnectionProvider] Connecting to remote browser', {
15531
- endpoint: endpointDebug,
15532
- connectTimeoutMs: this.remoteConnectTimeoutMs,
15533
- retries: this.remoteConnectRetries,
15534
- });
15535
- }
15536
- let attempts = 0;
15537
- try {
15538
- const connectResult = await retryWithBackoff(async (attempt) => {
15539
- attempts = attempt;
15540
- return await chromium.connect(wsEndpoint, {
15541
- timeout: this.remoteConnectTimeoutMs,
15542
- });
15543
- }, {
15544
- retries: this.remoteConnectRetries,
15545
- initialDelayMs: this.remoteConnectBackoffInitialMs,
15546
- maxDelayMs: this.remoteConnectBackoffMaxMs,
15547
- backoffFactor: this.remoteConnectBackoffFactor,
15548
- jitterRatio: this.remoteConnectJitterRatio,
15549
- signal: options.signal,
15550
- shouldRetry: (error) => isRemoteBrowserInfrastructureError(error),
15551
- onRetry: ({ attempt, delayMs, error }) => {
15552
- console.warn('[run_browser][retry]', {
15553
- tool: 'run_browser',
15554
- mode: 'remote-browser',
15555
- sessionId: options.sessionId || null,
15556
- event: 'remote_browser_connect_retry',
15557
- attempt,
15558
- delayMs,
15559
- endpoint: endpointDebug,
15560
- errorCode: extractNetworkErrorCode(error),
15561
- error: getErrorMessage(error),
15562
- });
15563
- },
15564
- random: this.random,
15565
- sleep: this.sleep,
15566
- });
15567
- const browser = connectResult.value;
15568
- // For remote connections, we need to create a new context
15569
- // Note: Remote browsers don't support persistent contexts
15570
- const context = await browser.newContext();
15571
- REMOTE_BROWSER_CONNECT_METRICS.success++;
15572
- console.info('[run_browser][metric]', {
15573
- tool: 'run_browser',
15574
- mode: 'remote-browser',
15575
- sessionId: options.sessionId || null,
15576
- event: 'remote_browser_connect_success',
15577
- attempts: connectResult.attempts,
15578
- connectDurationMs: connectResult.durationMs,
15579
- endpoint: endpointDebug,
15580
- counter: REMOTE_BROWSER_CONNECT_METRICS.success,
15581
- });
15582
- if (this.isVerbose) {
15583
- console.info('[BrowserConnectionProvider] Successfully connected to remote browser');
15584
- }
15585
- return context;
15586
- }
15587
- catch (error) {
15588
- REMOTE_BROWSER_CONNECT_METRICS.failure++;
15589
- const durationMs = Date.now() - startedAt;
15590
- const remoteInfraUnavailable = isRemoteBrowserInfrastructureError(error);
15591
- if (remoteInfraUnavailable) {
15592
- const remoteBrowserUnavailableError = new RemoteBrowserUnavailableError({
15593
- message: `Remote browser is unavailable. Could not establish a websocket connection.`,
15594
- debug: {
15595
- endpoint: endpointDebug,
15596
- attempts: Math.max(1, attempts),
15597
- connectTimeoutMs: this.remoteConnectTimeoutMs,
15598
- durationMs,
15599
- networkErrorCode: extractNetworkErrorCode(error),
15600
- originalMessage: getErrorMessage(error),
15601
- },
15602
- cause: error,
15603
- });
15604
- console.warn('[run_browser][metric]', {
15605
- tool: 'run_browser',
15606
- mode: 'remote-browser',
15607
- sessionId: options.sessionId || null,
15608
- event: 'remote_browser_connect_failure',
15609
- errorCode: remoteBrowserUnavailableError.code,
15610
- attempts: Math.max(1, attempts),
15611
- connectDurationMs: durationMs,
15612
- endpoint: endpointDebug,
15613
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
15614
- });
15615
- throw remoteBrowserUnavailableError;
15616
- }
15617
- console.error('[run_browser][metric]', {
15618
- tool: 'run_browser',
15619
- mode: 'remote-browser',
15620
- sessionId: options.sessionId || null,
15621
- event: 'remote_browser_connect_failure',
15622
- errorCode: 'REMOTE_BROWSER_CONNECT_ERROR',
15623
- attempts: Math.max(1, attempts),
15624
- connectDurationMs: durationMs,
15625
- endpoint: endpointDebug,
15626
- error: getErrorMessage(error),
15627
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
15628
- });
15629
- throw error;
15630
- }
15631
- }
15632
- }
15633
-
15634
- /**
15635
- * Singleton instance of the browser connection provider.
15636
- *
15637
- * @private internal cache for `$provideBrowserForServer`
15638
- */
15639
- let browserProvider = null;
15640
- /**
15641
- * Provides a browser context for server-side operations, with caching to reuse instances.
15642
- *
15643
- * This function supports both local and remote browser connections based on environment configuration.
15644
- * Use REMOTE_BROWSER_URL environment variable to configure a remote Playwright server.
15645
- *
15646
- * @param options - Optional runtime request options used for cancellation and logging context.
15647
- * @returns Browser context instance
15648
- */
15649
- async function $provideBrowserForServer(options = {}) {
15650
- if (browserProvider === null) {
15651
- browserProvider = new BrowserConnectionProvider({ isVerbose: false });
15652
- }
15653
- return await browserProvider.getBrowserContext(options);
15654
- }
15655
- /**
15656
- * TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
15657
- */
15658
-
15659
- /**
15660
- * Attempts to compute time-to-first-byte from Playwright response timing.
15661
- */
15662
- function resolveTimeToFirstByteMs(response) {
15663
- if (!response) {
15664
- return null;
15665
- }
15666
- try {
15667
- const timing = response.request().timing();
15668
- if (typeof (timing === null || timing === void 0 ? void 0 : timing.responseStart) === 'number' &&
15669
- typeof (timing === null || timing === void 0 ? void 0 : timing.startTime) === 'number' &&
15670
- timing.responseStart >= timing.startTime) {
15671
- return Math.round(timing.responseStart - timing.startTime);
15672
- }
15673
- }
15674
- catch (_a) {
15675
- return null;
15676
- }
15677
- return null;
15678
- }
15679
- /**
15680
- * Page open, action normalization and action execution helpers for `run_browser`.
15681
- *
15682
- * @private function of `run_browser`
15683
- */
15684
- const runBrowserWorkflow = {
15685
- /**
15686
- * Opens a new browser page and navigates to the requested URL.
15687
- */
15688
- async openPageWithUrl(options) {
15689
- runBrowserErrorHandling.assertNotAborted(options.signal, options.sessionId);
15690
- const connectStartedAt = Date.now();
15691
- const browserContext = await $provideBrowserForServer({
15692
- signal: options.signal,
15693
- sessionId: options.sessionId,
15694
- });
15695
- const connectDurationMs = Date.now() - connectStartedAt;
15696
- const page = await browserContext.newPage();
15697
- page.setDefaultNavigationTimeout(options.timeouts.navigationTimeoutMs);
15698
- page.setDefaultTimeout(options.timeouts.actionTimeoutMs);
15699
- const navigationStartedAt = Date.now();
15700
- try {
15701
- const navigationResponse = await page.goto(options.url, {
15702
- waitUntil: 'domcontentloaded',
15703
- timeout: options.timeouts.navigationTimeoutMs,
15704
- });
15705
- return {
15706
- page,
15707
- connectDurationMs,
15708
- initialNavigationDurationMs: Date.now() - navigationStartedAt,
15709
- timeToFirstByteMs: resolveTimeToFirstByteMs(navigationResponse),
15710
- };
15711
- }
15712
- catch (error) {
15713
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
15714
- message: `Failed to navigate to \`${options.url}\`.`,
15715
- debug: {
15716
- phase: 'initial-navigation',
15717
- url: options.url,
15718
- navigationTimeoutMs: options.timeouts.navigationTimeoutMs,
15719
- },
15720
- cause: error,
15721
- });
15722
- }
15723
- },
15724
- /**
15725
- * Validates and normalizes browser actions received from the model.
15726
- */
15727
- normalizeActions(actions) {
15728
- if (!actions || actions.length === 0) {
15729
- return [];
15730
- }
15731
- return actions.map((action, index) => this.normalizeAction(action, index));
15732
- },
15733
- /**
15734
- * Validates and normalizes a single action.
15735
- */
15736
- normalizeAction(action, index) {
15737
- var _a, _b, _c;
15738
- switch (action.type) {
15739
- case 'navigate': {
15740
- const url = String(action.value || '').trim();
15741
- if (!url) {
15742
- throw runBrowserErrorHandling.createRunBrowserValidationError({
15743
- message: spaceTrim$1(`Action ${index + 1}: \`navigate\` requires non-empty \`value\` URL.`),
15744
- debug: {
15745
- actionIndex: index + 1,
15746
- actionType: action.type,
15747
- },
15748
- });
15749
- }
15750
- return { type: 'navigate', url };
15751
- }
15752
- case 'click': {
15753
- const selector = String(action.selector || '').trim();
15754
- if (!selector) {
15755
- throw runBrowserErrorHandling.createRunBrowserValidationError({
15756
- message: spaceTrim$1(`Action ${index + 1}: \`click\` requires non-empty \`selector\`.`),
15757
- debug: {
15758
- actionIndex: index + 1,
15759
- actionType: action.type,
15760
- },
15761
- });
15762
- }
15763
- return { type: 'click', selector };
15764
- }
15765
- case 'type': {
15766
- const selector = String(action.selector || '').trim();
15767
- if (!selector) {
15768
- throw runBrowserErrorHandling.createRunBrowserValidationError({
15769
- message: spaceTrim$1(`Action ${index + 1}: \`type\` requires non-empty \`selector\`.`),
15770
- debug: {
15771
- actionIndex: index + 1,
15772
- actionType: action.type,
15773
- },
15774
- });
15775
- }
15776
- const text = String((_a = action.value) !== null && _a !== void 0 ? _a : '');
15777
- return { type: 'type', selector, text };
15778
- }
15779
- case 'wait': {
15780
- const requestedValue = Number.parseInt(String((_b = action.value) !== null && _b !== void 0 ? _b : runBrowserConstants.defaultWaitMs), 10);
15781
- const milliseconds = Number.isFinite(requestedValue)
15782
- ? Math.min(Math.max(requestedValue, 1), runBrowserConstants.maxWaitMs)
15783
- : runBrowserConstants.defaultWaitMs;
15784
- return { type: 'wait', milliseconds };
15785
- }
15786
- case 'scroll': {
15787
- const requestedValue = Number.parseInt(String((_c = action.value) !== null && _c !== void 0 ? _c : runBrowserConstants.defaultScrollPixels), 10);
15788
- const pixels = Number.isFinite(requestedValue) ? requestedValue : runBrowserConstants.defaultScrollPixels;
15789
- const rawSelector = String(action.selector || '').trim();
15790
- return { type: 'scroll', selector: rawSelector || null, pixels };
15791
- }
15792
- }
15793
- },
15794
- /**
15795
- * Executes one normalized browser action on a Playwright page.
15796
- */
15797
- async executeAction(options) {
15798
- const { page, action, actionIndex, timeouts, signal } = options;
15799
- runBrowserErrorHandling.assertNotAborted(signal, `action-${actionIndex}`);
15800
- try {
15801
- switch (action.type) {
15802
- case 'navigate':
15803
- await page.goto(action.url, {
15804
- waitUntil: 'domcontentloaded',
15805
- timeout: timeouts.navigationTimeoutMs,
15806
- });
15807
- return;
15808
- case 'click':
15809
- await page.locator(action.selector).first().click({ timeout: timeouts.actionTimeoutMs });
15810
- return;
15811
- case 'type':
15812
- await page.locator(action.selector).first().fill(action.text, { timeout: timeouts.actionTimeoutMs });
15813
- return;
15814
- case 'wait':
15815
- if (action.milliseconds > timeouts.actionTimeoutMs) {
15816
- throw runBrowserErrorHandling.createRunBrowserActionError({
15817
- message: `Action ${actionIndex}: \`wait\` exceeds action timeout (${timeouts.actionTimeoutMs}ms).`,
15818
- debug: {
15819
- actionIndex,
15820
- action,
15821
- actionTimeoutMs: timeouts.actionTimeoutMs,
15822
- },
15823
- });
15824
- }
15825
- await page.waitForTimeout(action.milliseconds);
15826
- return;
15827
- case 'scroll':
15828
- if (action.selector) {
15829
- await page
15830
- .locator(action.selector)
15831
- .first()
15832
- .scrollIntoViewIfNeeded({ timeout: timeouts.actionTimeoutMs });
15833
- }
15834
- await page.mouse.wheel(0, action.pixels);
15835
- return;
15836
- }
15837
- }
15838
- catch (error) {
15839
- if (runBrowserErrorHandling.isTaggedRunBrowserError(error)) {
15840
- throw error;
15841
- }
15842
- if (action.type === 'navigate') {
15843
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
15844
- message: `Action ${actionIndex}: failed to navigate to \`${action.url}\`.`,
15845
- debug: {
15846
- actionIndex,
15847
- action,
15848
- navigationTimeoutMs: timeouts.navigationTimeoutMs,
15849
- },
15850
- cause: error,
15851
- });
15852
- }
15853
- throw runBrowserErrorHandling.createRunBrowserActionError({
15854
- message: `Action ${actionIndex}: failed to execute \`${action.type}\`.`,
15855
- debug: {
15856
- actionIndex,
15857
- action,
15858
- actionTimeoutMs: timeouts.actionTimeoutMs,
15859
- },
15860
- cause: error,
15861
- });
15862
- }
15863
- },
15864
- };
15865
-
15866
- /**
15867
- * Summarizes one normalized browser action in user-facing language.
15868
- */
15869
- function formatRunBrowserActionSummary(action) {
15870
- switch (action.type) {
15871
- case 'navigate':
15872
- return `Navigate to ${action.url}`;
15873
- case 'click':
15874
- return `Click ${action.selector}`;
15875
- case 'type':
15876
- return `Type into ${action.selector}`;
15877
- case 'wait':
15878
- return `Wait ${action.milliseconds}ms`;
15879
- case 'scroll':
15880
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
15881
- }
15882
- }
15883
- /**
15884
- * Emits one incremental browser-tool update when a hidden chat-progress listener is attached.
15885
- */
15886
- function emitRunBrowserProgress(args, update) {
15887
- emitToolCallProgressFromToolArgs(args, update);
15888
- }
15889
- /**
15890
- * Returns the current timestamp in the branded ISO-8601 format used by tool-call logs.
15891
- */
15892
- function createRunBrowserLogTimestamp() {
15893
- return new Date().toISOString();
15894
- }
15895
- /**
15896
- * Executes non-graphical fallback scraping.
15897
- */
15898
- async function runFallbackScrape(url) {
15899
- return await fetchUrlContent(url);
15900
- }
15901
- /**
15902
- * Runs interactive browser automation through Playwright.
15903
- *
15904
- * @param args Tool arguments provided by the model.
15905
- * @param internalOptions Optional runtime options for cancellation.
15906
- * @returns Markdown summary with structured playback payload.
15907
- */
15908
- async function run_browser(args, internalOptions = {}) {
15909
- runBrowserObservability.incrementTotalRuns();
15910
- const startedAt = Date.now();
15911
- const sessionId = runBrowserRuntime.createRunBrowserSessionId();
15912
- const initialUrl = String(args.url || '').trim();
15913
- const mode = runBrowserRuntime.resolveExecutionMode();
15914
- const timeoutConfiguration = runBrowserRuntime.resolveTimeoutConfiguration(args.timeouts);
15915
- let page = null;
15916
- let connectDurationMs = null;
15917
- let initialNavigationDurationMs = null;
15918
- let timeToFirstByteMs = null;
15919
- try {
15920
- if (!initialUrl) {
15921
- throw runBrowserErrorHandling.createRunBrowserValidationError({
15922
- message: 'Missing required `url` argument.',
15923
- debug: {
15924
- field: 'url',
15925
- },
15926
- });
15927
- }
15928
- const normalizedActions = runBrowserWorkflow.normalizeActions(args.actions);
15929
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
15930
- const openedPage = await runBrowserWorkflow.openPageWithUrl({
15931
- url: initialUrl,
15932
- sessionId,
15933
- timeouts: timeoutConfiguration,
15934
- signal: internalOptions.signal,
15935
- });
15936
- page = openedPage.page;
15937
- connectDurationMs = openedPage.connectDurationMs;
15938
- initialNavigationDurationMs = openedPage.initialNavigationDurationMs;
15939
- timeToFirstByteMs = openedPage.timeToFirstByteMs;
15940
- emitRunBrowserProgress(args, {
15941
- state: 'PARTIAL',
15942
- log: {
15943
- createdAt: createRunBrowserLogTimestamp(),
15944
- kind: 'browser-session',
15945
- title: 'Browser ready',
15946
- message: 'Opened the initial page and started the browser session.',
15947
- payload: {
15948
- sessionId,
15949
- initialUrl,
15950
- connectDurationMs,
15951
- initialNavigationDurationMs,
15952
- timeToFirstByteMs,
15953
- },
15954
- },
15955
- });
15956
- const artifacts = [];
15957
- const initialArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
15958
- page,
15959
- sessionId,
15960
- label: 'Initial page',
15961
- fileSuffix: 'initial',
15962
- });
15963
- if (initialArtifact) {
15964
- artifacts.push(initialArtifact);
15965
- }
15966
- for (const [index, action] of normalizedActions.entries()) {
15967
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
15968
- emitRunBrowserProgress(args, {
15969
- state: 'PARTIAL',
15970
- log: {
15971
- createdAt: createRunBrowserLogTimestamp(),
15972
- kind: 'browser-action',
15973
- title: `Action ${index + 1} running`,
15974
- message: formatRunBrowserActionSummary(action),
15975
- payload: {
15976
- actionIndex: index + 1,
15977
- action,
15978
- phase: 'running',
15979
- },
15980
- },
15981
- });
15982
- await runBrowserWorkflow.executeAction({
15983
- page,
15984
- action,
15985
- actionIndex: index + 1,
15986
- timeouts: timeoutConfiguration,
15987
- signal: internalOptions.signal,
15988
- });
15989
- emitRunBrowserProgress(args, {
15990
- state: 'PARTIAL',
15991
- log: {
15992
- createdAt: createRunBrowserLogTimestamp(),
15993
- kind: 'browser-action',
15994
- title: `Action ${index + 1} finished`,
15995
- message: formatRunBrowserActionSummary(action),
15996
- payload: {
15997
- actionIndex: index + 1,
15998
- action,
15999
- phase: 'complete',
16000
- },
16001
- },
16002
- });
16003
- const actionArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
16004
- page,
16005
- sessionId,
16006
- label: `After action ${index + 1}`,
16007
- fileSuffix: `action-${String(index + 1).padStart(3, '0')}-${action.type}`,
16008
- actionIndex: index + 1,
16009
- action,
16010
- });
16011
- if (actionArtifact) {
16012
- artifacts.push(actionArtifact);
16013
- }
16014
- }
16015
- const snapshotPath = await runBrowserArtifacts.captureSnapshot(page, sessionId);
16016
- const finalUrl = page.url();
16017
- const finalTitle = await runBrowserArtifacts.getPageTitle(page);
16018
- if (snapshotPath) {
16019
- artifacts.push({
16020
- kind: 'screenshot',
16021
- label: 'Final page',
16022
- path: snapshotPath,
16023
- capturedAt: new Date().toISOString(),
16024
- url: finalUrl,
16025
- title: finalTitle,
16026
- });
16027
- }
16028
- const payload = runBrowserResultFormatting.createResultPayload({
16029
- sessionId,
16030
- mode,
16031
- modeUsed: 'remote-browser',
16032
- initialUrl,
16033
- finalUrl,
16034
- finalTitle,
16035
- executedActions: normalizedActions,
16036
- artifacts,
16037
- warning: null,
16038
- error: null,
16039
- fallbackContent: null,
16040
- timing: {
16041
- connectDurationMs,
16042
- initialNavigationDurationMs,
16043
- timeToFirstByteMs,
16044
- totalDurationMs: Date.now() - startedAt,
16045
- },
16046
- });
16047
- runBrowserObservability.logRunBrowserMetric({
16048
- event: 'run_browser_success',
16049
- sessionId,
16050
- mode: 'remote-browser',
16051
- payload: {
16052
- actions: normalizedActions.length,
16053
- connectDurationMs,
16054
- initialNavigationDurationMs,
16055
- timeToFirstByteMs,
16056
- },
16057
- });
16058
- return runBrowserResultFormatting.formatSuccessResult({
16059
- payload,
16060
- snapshotPath,
16061
- });
16062
- }
16063
- catch (error) {
16064
- const toolError = runBrowserErrorHandling.classifyRunBrowserToolError({
16065
- error,
16066
- sessionId,
16067
- mode,
16068
- });
16069
- const errorCodeCount = runBrowserObservability.incrementRunBrowserErrorCodeCounter(toolError.code);
16070
- if (runBrowserErrorHandling.isRemoteBrowserUnavailableCode(toolError.code) && initialUrl) {
16071
- const fallbackContent = await runFallbackScrape(initialUrl);
16072
- const { fallbackRuns, fallbackRate } = runBrowserObservability.incrementFallbackRunsAndGetMetrics();
16073
- emitRunBrowserProgress(args, {
16074
- state: 'PARTIAL',
16075
- log: {
16076
- createdAt: createRunBrowserLogTimestamp(),
16077
- kind: 'warning',
16078
- level: 'warning',
16079
- title: 'Fallback enabled',
16080
- message: 'Remote browser was unavailable, so fallback scraping was used instead.',
16081
- payload: {
16082
- errorCode: toolError.code,
16083
- initialUrl,
16084
- },
16085
- },
16086
- });
16087
- const payload = runBrowserResultFormatting.createResultPayload({
16088
- sessionId,
16089
- mode,
16090
- modeUsed: 'fallback',
16091
- initialUrl,
16092
- finalUrl: null,
16093
- finalTitle: null,
16094
- executedActions: [],
16095
- artifacts: [],
16096
- warning: runBrowserConstants.fallbackDynamicContentWarning,
16097
- error: toolError,
16098
- fallbackContent,
16099
- timing: {
16100
- connectDurationMs,
16101
- initialNavigationDurationMs,
16102
- timeToFirstByteMs,
16103
- totalDurationMs: Date.now() - startedAt,
16104
- },
16105
- });
16106
- runBrowserObservability.logRunBrowserMetric({
16107
- event: 'run_browser_fallback_used',
16108
- sessionId,
16109
- mode: 'fallback',
16110
- payload: {
16111
- errorCode: toolError.code,
16112
- errorCodeCount,
16113
- fallbackRuns,
16114
- totalRuns: runBrowserObservability.getTotalRuns(),
16115
- fallbackRate,
16116
- },
16117
- });
16118
- return runBrowserResultFormatting.formatFallbackResult({
16119
- payload,
16120
- fallbackContent,
16121
- requestedActions: Array.isArray(args.actions) ? args.actions.length : 0,
16122
- });
16123
- }
16124
- emitRunBrowserProgress(args, {
16125
- state: 'ERROR',
16126
- log: {
16127
- createdAt: createRunBrowserLogTimestamp(),
16128
- kind: 'error',
16129
- level: 'error',
16130
- title: 'Browser run failed',
16131
- message: toolError.message,
16132
- payload: {
16133
- code: toolError.code,
16134
- debug: toolError.debug,
16135
- },
16136
- },
16137
- });
16138
- const payload = runBrowserResultFormatting.createResultPayload({
16139
- sessionId,
16140
- mode,
16141
- modeUsed: 'remote-browser',
16142
- initialUrl,
16143
- finalUrl: page ? page.url() : null,
16144
- finalTitle: page ? await runBrowserArtifacts.getPageTitle(page) : null,
16145
- executedActions: [],
16146
- artifacts: [],
16147
- warning: null,
16148
- error: toolError,
16149
- fallbackContent: null,
16150
- timing: {
16151
- connectDurationMs,
16152
- initialNavigationDurationMs,
16153
- timeToFirstByteMs,
16154
- totalDurationMs: Date.now() - startedAt,
16155
- },
16156
- });
16157
- runBrowserObservability.logRunBrowserMetric({
16158
- event: 'run_browser_failed',
16159
- sessionId,
16160
- mode: 'remote-browser',
16161
- payload: {
16162
- errorCode: toolError.code,
16163
- errorCodeCount,
16164
- connectDurationMs,
16165
- initialNavigationDurationMs,
16166
- timeToFirstByteMs,
16167
- },
16168
- });
16169
- return runBrowserResultFormatting.formatErrorResult({
16170
- payload,
16171
- });
16172
- }
16173
- finally {
16174
- await runBrowserArtifacts.cleanupPage(page, sessionId);
16175
- }
16176
- }
16177
-
16178
- /**
16179
- * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
16180
- *
16181
- * This uses lazy `require` to keep the core package decoupled from Agents Server internals.
16182
- * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
16183
- *
16184
- * @private internal utility for USE BROWSER commitment
16185
- */
16186
- function resolveRunBrowserToolForNode() {
16187
- try {
16188
- // eslint-disable-next-line @typescript-eslint/no-var-requires
16189
- // const { run_browser } = require('../../../apps/agents-server/src/tools/run_browser');
16190
- if (typeof run_browser !== 'function') {
16191
- throw new Error('run_browser value is not a function but ' + typeof run_browser);
16192
- }
16193
- return run_browser;
16194
- }
16195
- catch (error) {
16196
- assertsError(error);
16197
- return async () => {
16198
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
16199
- \`run_browser\` tool is not available in this environment.
16200
- This commitment requires the Agents Server browser runtime with Playwright CLI.
16201
-
16202
- ${error.name}:
16203
- ${block(error.message)}
16204
- `));
16205
- };
16206
- }
16207
- }
16208
-
16209
- /**
16210
- * Resolves the server-side implementation of the send_email tool for Node.js environments.
16211
- *
16212
- * This uses a lazy require so the core package can still load even if the Agents Server
16213
- * module is unavailable. When the server tool cannot be resolved, a fallback implementation
16214
- * throws a helpful error message.
16215
- *
16216
- * @private internal utility for USE EMAIL commitment
16217
- */
16218
- function resolveSendEmailToolForNode() {
16219
- try {
16220
- // eslint-disable-next-line @typescript-eslint/no-var-requires
16221
- const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
16222
- if (typeof send_email !== 'function') {
16223
- throw new Error('send_email value is not a function but ' + typeof send_email);
16224
- }
16225
- return send_email;
16226
- }
16227
- catch (error) {
16228
- const normalizedError = error instanceof Error
16229
- ? error
16230
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
16231
- return async () => {
16232
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
16233
- \`send_email\` tool is not available in this environment.
16234
- This commitment requires Agents Server runtime with wallet-backed SMTP sending.
16235
-
16236
- ${normalizedError.name}:
16237
- ${block(normalizedError.message)}
16238
- `));
16239
- };
16240
- }
16241
- }
16242
-
16243
- /**
16244
- * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
16245
- *
16246
- * Uses lazy require so core package can load outside Agents Server.
16247
- *
16248
- * @private internal utility for USE SPAWN commitment
16249
- */
16250
- function resolveSpawnAgentToolForNode() {
16251
- try {
16252
- // eslint-disable-next-line @typescript-eslint/no-var-requires
16253
- const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
16254
- if (typeof spawn_agent !== 'function') {
16255
- throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
16256
- }
16257
- return spawn_agent;
16258
- }
16259
- catch (error) {
16260
- const normalizedError = error instanceof Error
16261
- ? error
16262
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
16263
- return async () => {
16264
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
16265
- \`spawn_agent\` tool is not available in this environment.
16266
- This commitment requires Agents Server runtime with agent persistence enabled.
16267
-
16268
- ${normalizedError.name}:
16269
- ${block(normalizedError.message)}
16270
- `));
16271
- };
16272
- }
16273
- }
16274
-
16275
- /**
16276
- * Generates a regex pattern to match a specific commitment
16277
- *
16278
- * Note: It always creates new Regex object
16279
- * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
16280
- *
16281
- * @private - TODO: [🧠] Maybe should be public?
16282
- */
16283
- function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
16284
- const allCommitments = [commitment, ...aliases];
16285
- const patterns = allCommitments.map((commitment) => {
16286
- const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
16287
- return escapedCommitment.split(/\s+/).join('\\s+');
16288
- });
16289
- const keywordPattern = patterns.join('|');
16290
- if (requiresContent) {
16291
- return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
16292
- }
16293
- else {
16294
- return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
16295
- }
16296
- }
16297
- /**
16298
- * Generates a regex pattern to match a specific commitment type
16299
- *
16300
- * Note: It just matches the type part of the commitment
16301
- * Note: It always creates new Regex object
16302
- * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
16303
- *
16304
- * @private
16305
- */
16306
- function createCommitmentTypeRegex(commitment, aliases = []) {
16307
- const allCommitments = [commitment, ...aliases];
16308
- const patterns = allCommitments.map((commitment) => {
16309
- const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
16310
- return escapedCommitment.split(/\s+/).join('\\s+');
16311
- });
16312
- const keywordPattern = patterns.join('|');
16313
- const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
16314
- return regex;
16315
- }
16316
-
16317
- /**
16318
- * Base implementation of CommitmentDefinition that provides common functionality
16319
- * Most commitments can extend this class and only override the applyToAgentModelRequirements method
16320
- *
16321
- * @private
16322
- */
16323
- class BaseCommitmentDefinition {
16324
- constructor(type, aliases = []) {
16325
- this.type = type;
16326
- this.aliases = aliases;
16327
- }
16328
- /**
16329
- * Whether this commitment requires content.
16330
- * If true, regex will match only if there is content after the commitment keyword.
16331
- * If false, regex will match even if there is no content.
16332
- */
16333
- get requiresContent() {
16334
- return true;
16335
- }
16336
- /**
16337
- * Optional UI/docs-only deprecation metadata.
16338
- */
16339
- get deprecation() {
16340
- return undefined;
16341
- }
16342
- /**
16343
- * Creates a regex pattern to match this commitment in agent source
16344
- * Uses the existing createCommitmentRegex function as internal helper
16345
- */
16346
- createRegex() {
16347
- return createCommitmentRegex(this.type, this.aliases, this.requiresContent);
16348
- }
16349
- /**
16350
- * Creates a regex pattern to match just the commitment type
16351
- * Uses the existing createCommitmentTypeRegex function as internal helper
16352
- */
16353
- createTypeRegex() {
16354
- return createCommitmentTypeRegex(this.type, this.aliases);
16355
- }
16356
- /**
16357
- * Helper method to create a new requirements object with updated system message
16358
- * This is commonly used by many commitments
16359
- */
16360
- updateSystemMessage(requirements, messageUpdate) {
16361
- const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
16362
- return {
16363
- ...requirements,
16364
- systemMessage: newMessage,
16365
- };
16366
- }
16367
- /**
16368
- * Helper method to append content to the system message
16369
- */
16370
- appendToSystemMessage(requirements, content, separator = '\n\n') {
16371
- return this.updateSystemMessage(requirements, (currentMessage) => {
16372
- if (!currentMessage.trim()) {
16373
- return content;
16374
- }
16375
- return currentMessage + separator + content;
16376
- });
16377
- }
16378
- /**
16379
- * Helper method to format one system-message section with an H2 heading.
16380
- *
16381
- * @param title - Section title without markdown prefix.
16382
- * @param content - Section body content.
16383
- * @returns Formatted section text using `##` heading.
16384
- */
16385
- createSystemMessageSection(title, content) {
16386
- const normalizedTitle = title.trim().replace(/^#+\s*/, '');
16387
- const normalizedContent = content.trim();
16388
- return normalizedContent ? `## ${normalizedTitle}\n${normalizedContent}` : `## ${normalizedTitle}`;
16389
- }
16390
- /**
16391
- * Helper method to create a new requirements object with updated prompt suffix
16392
- */
16393
- updatePromptSuffix(requirements, contentUpdate) {
16394
- const newSuffix = typeof contentUpdate === 'string' ? contentUpdate : contentUpdate(requirements.promptSuffix);
16395
- return {
16396
- ...requirements,
16397
- promptSuffix: newSuffix,
16398
- };
16399
- }
16400
- /**
16401
- * Helper method to append content to the prompt suffix
16402
- * Default separator is a single newline for bullet lists.
16403
- */
16404
- appendToPromptSuffix(requirements, content, separator = '\n') {
16405
- return this.updatePromptSuffix(requirements, (currentSuffix) => {
16406
- if (!currentSuffix.trim()) {
16407
- return content;
16408
- }
16409
- return `${currentSuffix}${separator}${content}`;
16410
- });
16411
- }
16412
- /**
16413
- * Helper method to add a comment section to the system message
16414
- * Comments are lines starting with # that will be removed from the final system message
16415
- * but can be useful for organizing and structuring the message during processing
16416
- */
16417
- addCommentSection(requirements, commentTitle, content, position = 'end') {
16418
- const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
16419
- if (position === 'beginning') {
16420
- return this.updateSystemMessage(requirements, (currentMessage) => {
16421
- if (!currentMessage.trim()) {
16422
- return commentSection;
16423
- }
16424
- return commentSection + '\n\n' + currentMessage;
16425
- });
16426
- }
16427
- else {
16428
- return this.appendToSystemMessage(requirements, commentSection);
16429
- }
16430
- }
16431
- /**
16432
- * Gets tool function implementations provided by this commitment
16433
- *
16434
- * When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
16435
- */
16436
- getToolFunctions() {
16437
- return {};
16438
- }
16439
- /**
16440
- * Gets human-readable titles for tool functions provided by this commitment
16441
- *
16442
- * This is used in the UI to show a user-friendly name instead of the technical function name.
16443
- */
16444
- getToolTitles() {
16445
- return {};
16446
- }
16447
- }
16448
-
16449
- /**
16450
- * ACTION commitment definition
16451
- *
16452
- * The ACTION commitment defines specific actions or capabilities that the agent can perform.
16453
- * This helps define what the agent is capable of doing and how it should approach tasks.
16454
- *
16455
- * Example usage in agent source:
16456
- *
16457
- * ```book
16458
- * ACTION Can generate code snippets and explain programming concepts
16459
- * ACTION Able to analyze data and provide insights
16460
- * ```
16461
- *
16462
- * @private [🪔] Maybe export the commitments through some package
16463
- */
16464
- class ActionCommitmentDefinition extends BaseCommitmentDefinition {
16465
- constructor(type = 'ACTION') {
16466
- super(type);
16467
- }
16468
- /**
16469
- * Short one-line description of ACTION.
16470
- */
16471
- get description() {
16472
- return 'Define agent capabilities and actions it can perform.';
16473
- }
16474
- /**
16475
- * Icon for this commitment.
16476
- */
16477
- get icon() {
16478
- return '⚡';
16479
- }
16480
- /**
16481
- * Markdown documentation for ACTION commitment.
14150
+ * Markdown documentation for ACTION commitment.
16482
14151
  */
16483
14152
  get documentation() {
16484
14153
  return spaceTrim$1(`
@@ -18038,6 +15707,110 @@ const parseMemoryToolArgs = {
18038
15707
  },
18039
15708
  };
18040
15709
 
15710
+ /**
15711
+ * Prompt parameter key used to pass hidden runtime context to tool execution.
15712
+ *
15713
+ * @private internal runtime wiring for commitment tools
15714
+ */
15715
+ const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
15716
+ /**
15717
+ * Hidden argument key used to pass runtime context into individual tool calls.
15718
+ *
15719
+ * @private internal runtime wiring for commitment tools
15720
+ */
15721
+ const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
15722
+ /**
15723
+ * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
15724
+ *
15725
+ * @private internal runtime wiring for commitment tools
15726
+ */
15727
+ const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
15728
+ /**
15729
+ * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
15730
+ *
15731
+ * @private internal runtime wiring for commitment tools
15732
+ */
15733
+ const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
15734
+ /**
15735
+ * Monotonic counter used for hidden progress-listener tokens.
15736
+ *
15737
+ * @private internal runtime wiring for commitment tools
15738
+ */
15739
+ let toolCallProgressListenerCounter = 0;
15740
+ /**
15741
+ * Active tool-progress listeners keyed by hidden execution token.
15742
+ *
15743
+ * @private internal runtime wiring for commitment tools
15744
+ */
15745
+ const toolCallProgressListeners = new Map();
15746
+ /**
15747
+ * Parses unknown runtime context payload into a normalized object.
15748
+ *
15749
+ * @private internal runtime wiring for commitment tools
15750
+ */
15751
+ function parseToolRuntimeContext(rawValue) {
15752
+ if (!rawValue) {
15753
+ return null;
15754
+ }
15755
+ let parsed = rawValue;
15756
+ if (typeof rawValue === 'string') {
15757
+ try {
15758
+ parsed = JSON.parse(rawValue);
15759
+ }
15760
+ catch (_a) {
15761
+ return null;
15762
+ }
15763
+ }
15764
+ if (!parsed || typeof parsed !== 'object') {
15765
+ return null;
15766
+ }
15767
+ return parsed;
15768
+ }
15769
+ /**
15770
+ * Reads runtime context attached to tool call arguments.
15771
+ *
15772
+ * @private internal runtime wiring for commitment tools
15773
+ */
15774
+ function readToolRuntimeContextFromToolArgs(args) {
15775
+ return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
15776
+ }
15777
+ /**
15778
+ * Serializes runtime context for prompt parameters.
15779
+ *
15780
+ * @private internal runtime wiring for commitment tools
15781
+ */
15782
+ function serializeToolRuntimeContext(context) {
15783
+ return JSON.stringify(context);
15784
+ }
15785
+ /**
15786
+ * Registers one in-memory listener that receives progress updates emitted by a running tool.
15787
+ *
15788
+ * The returned token is passed into script execution as a hidden argument so tool implementations
15789
+ * can stream progress without exposing extra parameters to the model.
15790
+ *
15791
+ * @param listener - Listener notified about tool progress.
15792
+ * @returns Hidden token used to route progress updates.
15793
+ * @private internal runtime wiring for commitment tools
15794
+ */
15795
+ function registerToolCallProgressListener(listener) {
15796
+ toolCallProgressListenerCounter += 1;
15797
+ const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
15798
+ toolCallProgressListeners.set(token, listener);
15799
+ return token;
15800
+ }
15801
+ /**
15802
+ * Unregisters one in-memory progress listener.
15803
+ *
15804
+ * @param token - Token previously created by `registerToolCallProgressListener`.
15805
+ * @private internal runtime wiring for commitment tools
15806
+ */
15807
+ function unregisterToolCallProgressListener(token) {
15808
+ toolCallProgressListeners.delete(token);
15809
+ }
15810
+ /**
15811
+ * Note: [💞] Ignore a discrepancy between file name and entity name
15812
+ */
15813
+
18041
15814
  /**
18042
15815
  * Resolves runtime context from hidden tool arguments.
18043
15816
  *
@@ -25746,9 +23519,9 @@ function createTimeoutSystemMessage(extraInstructions) {
25746
23519
  return spaceTrim$1((block) => `
25747
23520
  Timeout scheduling:
25748
23521
  - Use "set_timeout" to wake this same chat thread in the future.
25749
- - Timers are thread-scoped, not global for the whole agent.
23522
+ - Use "list_timeouts" to review timeouts across all chats for the same user+agent scope.
23523
+ - "cancel_timeout" accepts a timeout id from any chat in this same user+agent scope.
25750
23524
  - 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\`.
25751
- - Use "cancel_timeout" when a previously scheduled timeout is no longer relevant.
25752
23525
  - Do not claim a timer was set or cancelled unless the tool confirms it.
25753
23526
  ${block(extraInstructions)}
25754
23527
  `);
@@ -25809,13 +23582,6 @@ function parseToolExecutionEnvelope(rawValue) {
25809
23582
  * @private internal utility of USE TIMEOUT
25810
23583
  */
25811
23584
  function createDisabledTimeoutResult(action, message) {
25812
- if (action === 'set') {
25813
- return {
25814
- action,
25815
- status: 'disabled',
25816
- message,
25817
- };
25818
- }
25819
23585
  return {
25820
23586
  action,
25821
23587
  status: 'disabled',
@@ -25842,6 +23608,18 @@ function getTimeoutToolRuntimeAdapterOrDisabledResult(action, runtimeContext) {
25842
23608
  }
25843
23609
  }
25844
23610
 
23611
+ /**
23612
+ * Default number of rows returned by `list_timeouts`.
23613
+ *
23614
+ * @private internal USE TIMEOUT constant
23615
+ */
23616
+ const DEFAULT_LIST_TIMEOUTS_LIMIT = 20;
23617
+ /**
23618
+ * Hard cap for `list_timeouts` page size.
23619
+ *
23620
+ * @private internal USE TIMEOUT constant
23621
+ */
23622
+ const MAX_LIST_TIMEOUTS_LIMIT = 100;
25845
23623
  /**
25846
23624
  * Parses and validates `USE TIMEOUT` tool arguments.
25847
23625
  *
@@ -25876,6 +23654,31 @@ const parseTimeoutToolArgs = {
25876
23654
  }
25877
23655
  return { timeoutId };
25878
23656
  },
23657
+ /**
23658
+ * Parses `list_timeouts` input.
23659
+ */
23660
+ list(args) {
23661
+ if (args.includeFinished !== undefined && typeof args.includeFinished !== 'boolean') {
23662
+ throw new PipelineExecutionError(spaceTrim$1(`
23663
+ Timeout \`includeFinished\` must be a boolean when provided.
23664
+ `));
23665
+ }
23666
+ const parsedLimit = args.limit === undefined ? DEFAULT_LIST_TIMEOUTS_LIMIT : Math.floor(Number(args.limit));
23667
+ if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
23668
+ throw new PipelineExecutionError(spaceTrim$1(`
23669
+ Timeout \`limit\` must be a positive number.
23670
+ `));
23671
+ }
23672
+ if (parsedLimit > MAX_LIST_TIMEOUTS_LIMIT) {
23673
+ throw new PipelineExecutionError(spaceTrim$1(`
23674
+ Timeout \`limit\` must be at most \`${MAX_LIST_TIMEOUTS_LIMIT}\`.
23675
+ `));
23676
+ }
23677
+ return {
23678
+ includeFinished: args.includeFinished === true,
23679
+ limit: parsedLimit,
23680
+ };
23681
+ },
25879
23682
  };
25880
23683
 
25881
23684
  /**
@@ -25886,6 +23689,7 @@ const parseTimeoutToolArgs = {
25886
23689
  const TimeoutToolNames = {
25887
23690
  set: 'set_timeout',
25888
23691
  cancel: 'cancel_timeout',
23692
+ list: 'list_timeouts',
25889
23693
  };
25890
23694
 
25891
23695
  /**
@@ -25985,6 +23789,35 @@ function createTimeoutToolFunctions() {
25985
23789
  return JSON.stringify(result);
25986
23790
  }
25987
23791
  },
23792
+ async [TimeoutToolNames.list](args) {
23793
+ const runtimeContext = resolveTimeoutRuntimeContext(args);
23794
+ const { adapter, disabledResult } = getTimeoutToolRuntimeAdapterOrDisabledResult('list', runtimeContext);
23795
+ if (!adapter || disabledResult) {
23796
+ return JSON.stringify(disabledResult);
23797
+ }
23798
+ try {
23799
+ const parsedArgs = parseTimeoutToolArgs.list(args);
23800
+ const listedTimeouts = await adapter.listTimeouts(parsedArgs, runtimeContext);
23801
+ const result = {
23802
+ action: 'list',
23803
+ status: 'listed',
23804
+ items: listedTimeouts.items,
23805
+ total: listedTimeouts.total,
23806
+ };
23807
+ return createToolExecutionEnvelope({
23808
+ assistantMessage: listedTimeouts.total === 1 ? 'Found 1 timeout.' : `Found ${listedTimeouts.total} timeouts.`,
23809
+ toolResult: result,
23810
+ });
23811
+ }
23812
+ catch (error) {
23813
+ const result = {
23814
+ action: 'list',
23815
+ status: 'error',
23816
+ message: error instanceof Error ? error.message : String(error),
23817
+ };
23818
+ return JSON.stringify(result);
23819
+ }
23820
+ },
25988
23821
  };
25989
23822
  }
25990
23823
 
@@ -26018,26 +23851,45 @@ function createTimeoutTools(existingTools = []) {
26018
23851
  if (!tools.some((tool) => tool.name === TimeoutToolNames.cancel)) {
26019
23852
  tools.push({
26020
23853
  name: TimeoutToolNames.cancel,
26021
- description: 'Cancel one previously scheduled timeout in the current chat thread.',
23854
+ description: 'Cancel one previously scheduled timeout within the same user+agent scope, even if it was set in another chat.',
26022
23855
  parameters: {
26023
23856
  type: 'object',
26024
23857
  properties: {
26025
23858
  timeoutId: {
26026
23859
  type: 'string',
26027
- description: 'Identifier returned earlier by `set_timeout`.',
23860
+ description: 'Identifier returned earlier by `set_timeout` or `list_timeouts`.',
26028
23861
  },
26029
23862
  },
26030
23863
  required: ['timeoutId'],
26031
23864
  },
26032
23865
  });
26033
23866
  }
23867
+ if (!tools.some((tool) => tool.name === TimeoutToolNames.list)) {
23868
+ tools.push({
23869
+ name: TimeoutToolNames.list,
23870
+ description: 'List scheduled timeouts across all chats for this same user+agent scope so they can be reviewed or cancelled.',
23871
+ parameters: {
23872
+ type: 'object',
23873
+ properties: {
23874
+ includeFinished: {
23875
+ type: 'boolean',
23876
+ description: 'When true, include completed, failed, and cancelled rows in addition to active timeouts.',
23877
+ },
23878
+ limit: {
23879
+ type: 'number',
23880
+ description: 'Maximum number of rows to return (default 20, max 100).',
23881
+ },
23882
+ },
23883
+ },
23884
+ });
23885
+ }
26034
23886
  return tools;
26035
23887
  }
26036
23888
 
26037
23889
  /**
26038
23890
  * `USE TIMEOUT` commitment definition.
26039
23891
  *
26040
- * The `USE TIMEOUT` commitment enables thread-scoped timers that wake the same chat later.
23892
+ * The `USE TIMEOUT` commitment enables timeout wake-ups and scoped timeout management.
26041
23893
  *
26042
23894
  * @private [🪔] Maybe export the commitments through some package
26043
23895
  */
@@ -26052,7 +23904,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
26052
23904
  * Short one-line description of `USE TIMEOUT`.
26053
23905
  */
26054
23906
  get description() {
26055
- return 'Enable thread-scoped timers that can wake the same chat in the future.';
23907
+ return 'Enable timeout wake-ups plus scoped timeout listing/cancellation across chats.';
26056
23908
  }
26057
23909
  /**
26058
23910
  * Icon for this commitment.
@@ -26067,14 +23919,15 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
26067
23919
  return spaceTrim$1(`
26068
23920
  # USE TIMEOUT
26069
23921
 
26070
- Enables the agent to schedule thread-scoped timeout wake-ups.
23922
+ Enables timeout wake-ups and timeout management for the same user+agent scope.
26071
23923
 
26072
23924
  ## Key aspects
26073
23925
 
26074
23926
  - The agent uses \`set_timeout\` to schedule a future wake-up in the same chat thread.
26075
23927
  - The tool returns immediately while the timeout is stored and executed by the runtime later.
26076
23928
  - The wake-up arrives as a new user-like timeout message in the same conversation.
26077
- - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`.
23929
+ - The agent can inspect known timeouts via \`list_timeouts\`.
23930
+ - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`, including timeouts created in another chat.
26078
23931
  - Commitment content is treated as optional timeout policy instructions.
26079
23932
 
26080
23933
  ## Examples
@@ -26103,6 +23956,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
26103
23956
  return {
26104
23957
  [TimeoutToolNames.set]: 'Set timer',
26105
23958
  [TimeoutToolNames.cancel]: 'Cancel timer',
23959
+ [TimeoutToolNames.list]: 'List timers',
26106
23960
  };
26107
23961
  }
26108
23962
  /**
@@ -26937,6 +24791,324 @@ function getAllCommitmentsToolFunctionsForNode() {
26937
24791
  * TODO: [??] Unite `xxxForServer` and `xxxForNode` naming
26938
24792
  */
26939
24793
 
24794
+ /**
24795
+ * Normalize options for `execCommand` and `execCommands`
24796
+ *
24797
+ * Note: `$` is used to indicate that this function behaves differently according to `process.platform`
24798
+ *
24799
+ * @private internal utility of `execCommand` and `execCommands`
24800
+ */
24801
+ function $execCommandNormalizeOptions(options) {
24802
+ var _a, _b, _c, _d;
24803
+ let command;
24804
+ let cwd;
24805
+ let crashOnError;
24806
+ let args = [];
24807
+ let timeout;
24808
+ let isVerbose;
24809
+ let env;
24810
+ if (typeof options === 'string') {
24811
+ // TODO: [1] DRY default values
24812
+ command = options;
24813
+ cwd = process.cwd();
24814
+ crashOnError = true;
24815
+ timeout = Infinity; // <- TODO: [⏳]
24816
+ isVerbose = DEFAULT_IS_VERBOSE;
24817
+ env = undefined;
24818
+ }
24819
+ else {
24820
+ /*
24821
+ TODO:
24822
+ if ((options as any).commands !== undefined) {
24823
+ commands = (options as any).commands;
24824
+ } else {
24825
+ commands = [(options as any).command];
24826
+ }
24827
+ */
24828
+ // TODO: [1] DRY default values
24829
+ command = options.command;
24830
+ cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
24831
+ crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
24832
+ timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
24833
+ isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
24834
+ env = options.env;
24835
+ }
24836
+ // TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
24837
+ const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
24838
+ .map(([match]) => match)
24839
+ .filter((arg) => arg !== '');
24840
+ if (_.length > 1) {
24841
+ [command, ...args] = _;
24842
+ }
24843
+ if (options.args) {
24844
+ args = [...args, ...options.args];
24845
+ }
24846
+ let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
24847
+ if (['ts-node'].includes(humanReadableCommand)) {
24848
+ humanReadableCommand += ` ${args[1]}`;
24849
+ }
24850
+ if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
24851
+ command = `${command}.cmd`;
24852
+ }
24853
+ return { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose, env };
24854
+ }
24855
+ // TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
24856
+
24857
+ /**
24858
+ * Run one command in a shell
24859
+ *
24860
+ *
24861
+ * Note: There are 2 similar functions in the codebase:
24862
+ * - `$execCommand` which runs a single command
24863
+ * - `$execCommands` which runs multiple commands
24864
+ * Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
24865
+ *
24866
+ * @public exported from `@promptbook/node`
24867
+ */
24868
+ function $execCommand(options) {
24869
+ if (!$isRunningInNode()) {
24870
+ throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
24871
+ }
24872
+ return new Promise((resolve, reject) => {
24873
+ // eslint-disable-next-line prefer-const
24874
+ const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
24875
+ if (timeout !== Infinity) {
24876
+ // TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
24877
+ forTime(timeout).then(() => {
24878
+ if (crashOnError) {
24879
+ reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
24880
+ }
24881
+ else {
24882
+ console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
24883
+ // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
24884
+ resolve('Command exceeded time limit');
24885
+ }
24886
+ });
24887
+ }
24888
+ if (isVerbose) {
24889
+ console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
24890
+ }
24891
+ try {
24892
+ const commandProcess = spawn(command, args, {
24893
+ cwd,
24894
+ shell: true,
24895
+ env: env ? { ...process.env, ...env } : process.env,
24896
+ });
24897
+ if (isVerbose) {
24898
+ commandProcess.on('message', (message) => {
24899
+ console.info({ message });
24900
+ });
24901
+ }
24902
+ const output = [];
24903
+ commandProcess.stdout.on('data', (stdout) => {
24904
+ output.push(stdout.toString());
24905
+ if (isVerbose) {
24906
+ console.info(stdout.toString());
24907
+ }
24908
+ });
24909
+ commandProcess.stderr.on('data', (stderr) => {
24910
+ output.push(stderr.toString());
24911
+ if (isVerbose && stderr.toString().trim()) {
24912
+ console.warn(stderr.toString());
24913
+ // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
24914
+ }
24915
+ });
24916
+ const finishWithCode = (code) => {
24917
+ if (code !== 0) {
24918
+ if (crashOnError) {
24919
+ reject(new Error(output.join('\n').trim() ||
24920
+ `Command "${humanReadableCommand}" exited with code ${code}`));
24921
+ }
24922
+ else {
24923
+ if (isVerbose) {
24924
+ console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
24925
+ // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
24926
+ }
24927
+ resolve(spaceTrim$1(output.join('\n')));
24928
+ }
24929
+ }
24930
+ else {
24931
+ resolve(spaceTrim$1(output.join('\n')));
24932
+ }
24933
+ };
24934
+ commandProcess.on('close', finishWithCode);
24935
+ commandProcess.on('exit', finishWithCode);
24936
+ commandProcess.on('disconnect', () => {
24937
+ // Note: Unexpected disconnection should always result in rejection
24938
+ reject(new Error(`Command "${humanReadableCommand}" disconnected`));
24939
+ });
24940
+ commandProcess.on('error', (error) => {
24941
+ if (crashOnError) {
24942
+ reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
24943
+ }
24944
+ else {
24945
+ if (isVerbose) {
24946
+ console.warn(error);
24947
+ // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
24948
+ }
24949
+ resolve(spaceTrim$1(output.join('\n')));
24950
+ }
24951
+ });
24952
+ }
24953
+ catch (error) {
24954
+ // Note: Unexpected error in sync code should always result in rejection
24955
+ reject(error);
24956
+ }
24957
+ });
24958
+ }
24959
+ /**
24960
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
24961
+ */
24962
+
24963
+ /**
24964
+ * Attempts to locate the specified application on a Linux system using the 'which' command.
24965
+ * Returns the path to the executable if found, or null otherwise.
24966
+ *
24967
+ * @private within the repository
24968
+ */
24969
+ async function locateAppOnLinux({ linuxWhich, }) {
24970
+ try {
24971
+ const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
24972
+ return result.trim();
24973
+ }
24974
+ catch (error) {
24975
+ assertsError(error);
24976
+ return null;
24977
+ }
24978
+ }
24979
+ /**
24980
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
24981
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
24982
+ */
24983
+
24984
+ /**
24985
+ * Checks if the file is executable
24986
+ *
24987
+ * @private within the repository
24988
+ */
24989
+ async function isExecutable(path, fs) {
24990
+ try {
24991
+ await fs.access(path, fs.constants.X_OK);
24992
+ return true;
24993
+ }
24994
+ catch (error) {
24995
+ return false;
24996
+ }
24997
+ }
24998
+ /**
24999
+ * Note: Not [~🟢~] because it is not directly dependent on `fs
25000
+ * TODO: [🖇] What about symlinks?
25001
+ */
25002
+
25003
+ // Note: Module `userhome` has no types available, so it is imported using `require`
25004
+ // @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
25005
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
25006
+ const userhome = require('userhome');
25007
+ /**
25008
+ * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
25009
+ * Returns the path to the executable if found, or null otherwise.
25010
+ *
25011
+ * @private within the repository
25012
+ */
25013
+ async function locateAppOnMacOs({ macOsName, }) {
25014
+ try {
25015
+ const toExec = `/Contents/MacOS/${macOsName}`;
25016
+ const regPath = `/Applications/${macOsName}.app` + toExec;
25017
+ const altPath = userhome(regPath.slice(1));
25018
+ if (await isExecutable(regPath, $provideFilesystemForNode())) {
25019
+ return regPath;
25020
+ }
25021
+ else if (await isExecutable(altPath, $provideFilesystemForNode())) {
25022
+ return altPath;
25023
+ }
25024
+ const result = await $execCommand({
25025
+ crashOnError: true,
25026
+ command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
25027
+ });
25028
+ return result.trim() + toExec;
25029
+ }
25030
+ catch (error) {
25031
+ assertsError(error);
25032
+ return null;
25033
+ }
25034
+ }
25035
+ /**
25036
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
25037
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
25038
+ */
25039
+
25040
+ /**
25041
+ * Attempts to locate the specified application on a Windows system by searching common installation directories.
25042
+ * Returns the path to the executable if found, or null otherwise.
25043
+ *
25044
+ * @private within the repository
25045
+ */
25046
+ async function locateAppOnWindows({ appName, windowsSuffix, }) {
25047
+ try {
25048
+ const prefixes = [
25049
+ process.env.LOCALAPPDATA,
25050
+ join(process.env.LOCALAPPDATA || '', 'Programs'),
25051
+ process.env.PROGRAMFILES,
25052
+ process.env['PROGRAMFILES(X86)'],
25053
+ ];
25054
+ for (const prefix of prefixes) {
25055
+ const path = prefix + windowsSuffix;
25056
+ if (await isExecutable(path, $provideFilesystemForNode())) {
25057
+ return path;
25058
+ }
25059
+ }
25060
+ throw new Error(`Can not locate app ${appName} on Windows.`);
25061
+ }
25062
+ catch (error) {
25063
+ assertsError(error);
25064
+ return null;
25065
+ }
25066
+ }
25067
+ /**
25068
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
25069
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
25070
+ */
25071
+
25072
+ /**
25073
+ * Locates an application on the system
25074
+ *
25075
+ * @private within the repository
25076
+ */
25077
+ function locateApp(options) {
25078
+ if (!$isRunningInNode()) {
25079
+ throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
25080
+ }
25081
+ const { appName, linuxWhich, windowsSuffix, macOsName } = options;
25082
+ if (process.platform === 'win32') {
25083
+ if (windowsSuffix) {
25084
+ return locateAppOnWindows({ appName, windowsSuffix });
25085
+ }
25086
+ else {
25087
+ throw new Error(`${appName} is not available on Windows.`);
25088
+ }
25089
+ }
25090
+ else if (process.platform === 'darwin') {
25091
+ if (macOsName) {
25092
+ return locateAppOnMacOs({ macOsName });
25093
+ }
25094
+ else {
25095
+ throw new Error(`${appName} is not available on macOS.`);
25096
+ }
25097
+ }
25098
+ else {
25099
+ if (linuxWhich) {
25100
+ return locateAppOnLinux({ linuxWhich });
25101
+ }
25102
+ else {
25103
+ throw new Error(`${appName} is not available on Linux.`);
25104
+ }
25105
+ }
25106
+ }
25107
+ /**
25108
+ * TODO: [🧠][♿] Maybe export through `@promptbook/node`
25109
+ * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
25110
+ */
25111
+
26940
25112
  /**
26941
25113
  * Locates the LibreOffice executable on the current system by searching platform-specific paths.
26942
25114
  * Returns the path to the executable if found, or null otherwise.
@@ -27083,13 +25255,13 @@ function $registeredLlmToolsMessage() {
27083
25255
  });
27084
25256
  const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
27085
25257
  if (metadata.length === 0) {
27086
- return spaceTrim$2((block) => `
25258
+ return spaceTrim$1((block) => `
27087
25259
  No LLM providers are available.
27088
25260
 
27089
25261
  ${block(usedEnvMessage)}
27090
25262
  `);
27091
25263
  }
27092
- return spaceTrim$2((block) => `
25264
+ return spaceTrim$1((block) => `
27093
25265
 
27094
25266
  ${block(usedEnvMessage)}
27095
25267
 
@@ -27135,7 +25307,7 @@ function $registeredLlmToolsMessage() {
27135
25307
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
27136
25308
  }
27137
25309
  }
27138
- let providerMessage = spaceTrim$2(`
25310
+ let providerMessage = spaceTrim$1(`
27139
25311
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
27140
25312
  ${morePieces.join('; ')}
27141
25313
  `);
@@ -27257,7 +25429,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
27257
25429
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
27258
25430
  if (registeredItem === undefined) {
27259
25431
  // console.log('$llmToolsRegister.list()', $llmToolsRegister.list());
27260
- throw new Error(spaceTrim$2((block) => `
25432
+ throw new Error(spaceTrim$1((block) => `
27261
25433
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
27262
25434
  Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
27263
25435
 
@@ -27325,14 +25497,14 @@ async function $provideLlmToolsFromEnv(options = {}) {
27325
25497
  const configuration = await $provideLlmToolsConfigurationFromEnv();
27326
25498
  if (configuration.length === 0) {
27327
25499
  if ($llmToolsMetadataRegister.list().length === 0) {
27328
- throw new UnexpectedError(spaceTrim$2((block) => `
25500
+ throw new UnexpectedError(spaceTrim$1((block) => `
27329
25501
  No LLM tools registered, this is probably a bug in the Promptbook library
27330
25502
 
27331
25503
  ${block($registeredLlmToolsMessage())}}
27332
25504
  `));
27333
25505
  }
27334
25506
  // TODO: [🥃]
27335
- throw new Error(spaceTrim$2((block) => `
25507
+ throw new Error(spaceTrim$1((block) => `
27336
25508
  No LLM tools found in the environment
27337
25509
 
27338
25510
  ${block($registeredLlmToolsMessage())}}
@@ -27484,7 +25656,7 @@ class JavascriptEvalExecutionTools {
27484
25656
  }
27485
25657
  // Note: [💎]
27486
25658
  // Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
27487
- const spaceTrim = (_) => spaceTrim$2(_);
25659
+ const spaceTrim = (_) => _spaceTrim(_);
27488
25660
  $preserve(spaceTrim);
27489
25661
  const removeQuotes$1 = removeQuotes;
27490
25662
  $preserve(removeQuotes$1);
@@ -27575,7 +25747,7 @@ class JavascriptEvalExecutionTools {
27575
25747
  .join('\n');
27576
25748
  // script = templateParameters(script, parameters);
27577
25749
  // <- TODO: [🧠][🥳] Should be this is one of two variants how to use parameters in script
27578
- const statementToEvaluate = spaceTrim$2((block) => `
25750
+ const statementToEvaluate = _spaceTrim((block) => `
27579
25751
 
27580
25752
  // Build-in functions:
27581
25753
  ${block(buildinFunctionsStatement)}
@@ -27590,7 +25762,7 @@ class JavascriptEvalExecutionTools {
27590
25762
  (async ()=>{ ${script} })()
27591
25763
  `);
27592
25764
  if (this.options.isVerbose) {
27593
- console.info(spaceTrim$2((block) => `
25765
+ console.info(_spaceTrim((block) => `
27594
25766
  🚀 Evaluating ${scriptLanguage} script:
27595
25767
 
27596
25768
  ${block(statementToEvaluate)}`));
@@ -27599,7 +25771,7 @@ class JavascriptEvalExecutionTools {
27599
25771
  try {
27600
25772
  result = await eval(statementToEvaluate);
27601
25773
  if (this.options.isVerbose) {
27602
- console.info(spaceTrim$2((block) => `
25774
+ console.info(_spaceTrim((block) => `
27603
25775
  🚀 Script evaluated successfully, result:
27604
25776
  ${block(valueToString(result))}
27605
25777
  `));
@@ -27618,7 +25790,7 @@ class JavascriptEvalExecutionTools {
27618
25790
  To: [PipelineExecutionError: Parameter `{thing}` is not defined],
27619
25791
  */
27620
25792
  if (!statementToEvaluate.includes(undefinedName + '(')) {
27621
- throw new PipelineExecutionError(spaceTrim$2((block) => `
25793
+ throw new PipelineExecutionError(_spaceTrim((block) => `
27622
25794
 
27623
25795
  Parameter \`{${undefinedName}}\` is not defined
27624
25796
 
@@ -27640,7 +25812,7 @@ class JavascriptEvalExecutionTools {
27640
25812
  `));
27641
25813
  }
27642
25814
  else {
27643
- throw new PipelineExecutionError(spaceTrim$2((block) => `
25815
+ throw new PipelineExecutionError(_spaceTrim((block) => `
27644
25816
  Function ${undefinedName}() is not defined
27645
25817
 
27646
25818
  - Make sure that the function is one of built-in functions
@@ -27893,7 +26065,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
27893
26065
  catch (error) {
27894
26066
  assertsError(error);
27895
26067
  // TODO: [7] DRY
27896
- const wrappedErrorMessage = spaceTrim$2((block) => `
26068
+ const wrappedErrorMessage = spaceTrim$1((block) => `
27897
26069
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
27898
26070
 
27899
26071
  Original error message:
@@ -27928,7 +26100,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
27928
26100
  pipeline = { ...pipeline, pipelineUrl };
27929
26101
  }
27930
26102
  else if (!pipeline.pipelineUrl.startsWith(rootUrl)) {
27931
- throw new PipelineUrlError(spaceTrim$2(`
26103
+ throw new PipelineUrlError(spaceTrim$1(`
27932
26104
  Pipeline with URL ${pipeline.pipelineUrl} is not a child of the root URL ${rootUrl} 🍏
27933
26105
 
27934
26106
  File:
@@ -27966,7 +26138,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
27966
26138
  }
27967
26139
  else {
27968
26140
  const existing = collection.get(pipeline.pipelineUrl);
27969
- throw new PipelineUrlError(spaceTrim$2(`
26141
+ throw new PipelineUrlError(spaceTrim$1(`
27970
26142
  Pipeline with URL ${pipeline.pipelineUrl} is already in the collection 🍏
27971
26143
 
27972
26144
  Conflicting files:
@@ -27984,7 +26156,7 @@ async function createPipelineCollectionFromDirectory(rootPath, tools, options) {
27984
26156
  catch (error) {
27985
26157
  assertsError(error);
27986
26158
  // TODO: [7] DRY
27987
- const wrappedErrorMessage = spaceTrim$2((block) => `
26159
+ const wrappedErrorMessage = spaceTrim$1((block) => `
27988
26160
  ${error.name} in pipeline ${fileName.split('\\').join('/')}⁠:
27989
26161
 
27990
26162
  Original error message:
@@ -28042,7 +26214,7 @@ async function $provideScriptingForNode(options) {
28042
26214
  */
28043
26215
  function stringifyPipelineJson(pipeline) {
28044
26216
  if (!isSerializableAsJson(pipeline)) {
28045
- throw new UnexpectedError(spaceTrim$2(`
26217
+ throw new UnexpectedError(spaceTrim$1(`
28046
26218
  Cannot stringify the pipeline, because it is not serializable as JSON
28047
26219
 
28048
26220
  There can be multiple reasons:
@@ -28221,7 +26393,7 @@ function validateBook(source) {
28221
26393
  * @deprecated Use `$generateBookBoilerplate` instead
28222
26394
  * @public exported from `@promptbook/core`
28223
26395
  */
28224
- padBook(validateBook(spaceTrim$2(`
26396
+ padBook(validateBook(spaceTrim$1(`
28225
26397
  AI Avatar
28226
26398
 
28227
26399
  PERSONA A friendly AI assistant that helps you with your tasks
@@ -28270,7 +26442,7 @@ function book(strings, ...values) {
28270
26442
  const bookString = prompt(strings, ...values).toString();
28271
26443
  if (!isValidPipelineString(bookString)) {
28272
26444
  // TODO: Make the CustomError for this
28273
- throw new Error(spaceTrim$2(`
26445
+ throw new Error(spaceTrim$1(`
28274
26446
  The string is not a valid pipeline string
28275
26447
 
28276
26448
  book\`
@@ -28280,7 +26452,7 @@ function book(strings, ...values) {
28280
26452
  }
28281
26453
  if (!isValidBook(bookString)) {
28282
26454
  // TODO: Make the CustomError for this
28283
- throw new Error(spaceTrim$2(`
26455
+ throw new Error(spaceTrim$1(`
28284
26456
  The string is not a valid book
28285
26457
 
28286
26458
  book\`
@@ -28564,7 +26736,7 @@ function computeAgentHash(agentSource) {
28564
26736
  * @public exported from `@promptbook/core`
28565
26737
  */
28566
26738
  function normalizeAgentName(rawAgentName) {
28567
- return titleToName(spaceTrim$2(rawAgentName));
26739
+ return titleToName(spaceTrim$1(rawAgentName));
28568
26740
  }
28569
26741
 
28570
26742
  /**
@@ -29008,7 +27180,7 @@ function parseAgentSource(agentSource) {
29008
27180
  continue;
29009
27181
  }
29010
27182
  if (commitment.type === 'FROM') {
29011
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
27183
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
29012
27184
  if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
29013
27185
  continue;
29014
27186
  }
@@ -29031,7 +27203,7 @@ function parseAgentSource(agentSource) {
29031
27203
  continue;
29032
27204
  }
29033
27205
  if (commitment.type === 'IMPORT') {
29034
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
27206
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
29035
27207
  let label = content;
29036
27208
  let iconName = 'ExternalLink'; // Import remote
29037
27209
  try {
@@ -29069,7 +27241,7 @@ function parseAgentSource(agentSource) {
29069
27241
  continue;
29070
27242
  }
29071
27243
  if (commitment.type === 'KNOWLEDGE') {
29072
- const content = spaceTrim$2(commitment.content);
27244
+ const content = spaceTrim$1(commitment.content);
29073
27245
  const extractedUrls = extractUrlsFromText(content);
29074
27246
  let label = content;
29075
27247
  let iconName = 'Book';
@@ -29128,7 +27300,7 @@ function parseAgentSource(agentSource) {
29128
27300
  continue;
29129
27301
  }
29130
27302
  if (commitment.type === 'META LINK') {
29131
- const linkValue = spaceTrim$2(commitment.content);
27303
+ const linkValue = spaceTrim$1(commitment.content);
29132
27304
  links.push(linkValue);
29133
27305
  meta.link = linkValue;
29134
27306
  continue;
@@ -29138,11 +27310,11 @@ function parseAgentSource(agentSource) {
29138
27310
  continue;
29139
27311
  }
29140
27312
  if (commitment.type === 'META IMAGE') {
29141
- meta.image = spaceTrim$2(commitment.content);
27313
+ meta.image = spaceTrim$1(commitment.content);
29142
27314
  continue;
29143
27315
  }
29144
27316
  if (commitment.type === 'META DESCRIPTION') {
29145
- meta.description = spaceTrim$2(commitment.content);
27317
+ meta.description = spaceTrim$1(commitment.content);
29146
27318
  continue;
29147
27319
  }
29148
27320
  if (commitment.type === 'META DISCLAIMER') {
@@ -29150,7 +27322,7 @@ function parseAgentSource(agentSource) {
29150
27322
  continue;
29151
27323
  }
29152
27324
  if (commitment.type === 'META INPUT PLACEHOLDER') {
29153
- meta.inputPlaceholder = spaceTrim$2(commitment.content);
27325
+ meta.inputPlaceholder = spaceTrim$1(commitment.content);
29154
27326
  continue;
29155
27327
  }
29156
27328
  if (commitment.type === 'MESSAGE SUFFIX') {
@@ -29166,7 +27338,7 @@ function parseAgentSource(agentSource) {
29166
27338
  continue;
29167
27339
  }
29168
27340
  if (commitment.type === 'META VOICE') {
29169
- meta.voice = spaceTrim$2(commitment.content);
27341
+ meta.voice = spaceTrim$1(commitment.content);
29170
27342
  continue;
29171
27343
  }
29172
27344
  if (commitment.type !== 'META') {
@@ -29175,10 +27347,10 @@ function parseAgentSource(agentSource) {
29175
27347
  // Parse META commitments - format is "META TYPE content"
29176
27348
  const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
29177
27349
  if (metaTypeRaw === 'LINK') {
29178
- links.push(spaceTrim$2(commitment.content.substring(metaTypeRaw.length)));
27350
+ links.push(spaceTrim$1(commitment.content.substring(metaTypeRaw.length)));
29179
27351
  }
29180
27352
  const metaType = normalizeTo_camelCase(metaTypeRaw);
29181
- meta[metaType] = spaceTrim$2(commitment.content.substring(metaTypeRaw.length));
27353
+ meta[metaType] = spaceTrim$1(commitment.content.substring(metaTypeRaw.length));
29182
27354
  }
29183
27355
  // Generate fullname fallback if no meta fullname specified
29184
27356
  if (!meta.fullname) {
@@ -29209,7 +27381,7 @@ function parseAgentSource(agentSource) {
29209
27381
  * @returns The content with normalized separators
29210
27382
  */
29211
27383
  function normalizeSeparator(content) {
29212
- const trimmed = spaceTrim$2(content);
27384
+ const trimmed = spaceTrim$1(content);
29213
27385
  if (trimmed.includes(',')) {
29214
27386
  return trimmed;
29215
27387
  }
@@ -29222,7 +27394,7 @@ function normalizeSeparator(content) {
29222
27394
  * @returns Normalized domain or a trimmed fallback.
29223
27395
  */
29224
27396
  function normalizeMetaDomain(content) {
29225
- const trimmed = spaceTrim$2(content);
27397
+ const trimmed = spaceTrim$1(content);
29226
27398
  return normalizeDomainForMatching(trimmed) || trimmed.toLowerCase();
29227
27399
  }
29228
27400
  /**
@@ -30482,7 +28654,7 @@ function pricing(value) {
30482
28654
  /**
30483
28655
  * List of available OpenAI models with pricing
30484
28656
  *
30485
- * Note: Synced with official API docs at 2025-11-19
28657
+ * Note: Synced with official API docs at 2026-03-22
30486
28658
  *
30487
28659
  * @see https://platform.openai.com/docs/models/
30488
28660
  * @see https://openai.com/api/pricing/
@@ -30604,8 +28776,8 @@ const OPENAI_MODELS = exportJson({
30604
28776
  modelName: 'gpt-4.1',
30605
28777
  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.',
30606
28778
  pricing: {
30607
- prompt: pricing(`$3.00 / 1M tokens`),
30608
- output: pricing(`$12.00 / 1M tokens`),
28779
+ prompt: pricing(`$2.00 / 1M tokens`),
28780
+ output: pricing(`$8.00 / 1M tokens`),
30609
28781
  },
30610
28782
  },
30611
28783
  /**/
@@ -30616,8 +28788,8 @@ const OPENAI_MODELS = exportJson({
30616
28788
  modelName: 'gpt-4.1-mini',
30617
28789
  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.',
30618
28790
  pricing: {
30619
- prompt: pricing(`$0.80 / 1M tokens`),
30620
- output: pricing(`$3.20 / 1M tokens`),
28791
+ prompt: pricing(`$0.40 / 1M tokens`),
28792
+ output: pricing(`$1.60 / 1M tokens`),
30621
28793
  },
30622
28794
  },
30623
28795
  /**/
@@ -30628,8 +28800,8 @@ const OPENAI_MODELS = exportJson({
30628
28800
  modelName: 'gpt-4.1-nano',
30629
28801
  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.',
30630
28802
  pricing: {
30631
- prompt: pricing(`$0.20 / 1M tokens`),
30632
- output: pricing(`$0.80 / 1M tokens`),
28803
+ prompt: pricing(`$0.10 / 1M tokens`),
28804
+ output: pricing(`$0.40 / 1M tokens`),
30633
28805
  },
30634
28806
  },
30635
28807
  /**/
@@ -30640,8 +28812,8 @@ const OPENAI_MODELS = exportJson({
30640
28812
  modelName: 'o3',
30641
28813
  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.',
30642
28814
  pricing: {
30643
- prompt: pricing(`$15.00 / 1M tokens`),
30644
- output: pricing(`$60.00 / 1M tokens`),
28815
+ prompt: pricing(`$2.00 / 1M tokens`),
28816
+ output: pricing(`$8.00 / 1M tokens`),
30645
28817
  },
30646
28818
  },
30647
28819
  /**/
@@ -30652,8 +28824,8 @@ const OPENAI_MODELS = exportJson({
30652
28824
  modelName: 'o3-pro',
30653
28825
  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.',
30654
28826
  pricing: {
30655
- prompt: pricing(`$30.00 / 1M tokens`),
30656
- output: pricing(`$120.00 / 1M tokens`),
28827
+ prompt: pricing(`$20.00 / 1M tokens`),
28828
+ output: pricing(`$80.00 / 1M tokens`),
30657
28829
  },
30658
28830
  },
30659
28831
  /**/
@@ -30664,8 +28836,8 @@ const OPENAI_MODELS = exportJson({
30664
28836
  modelName: 'o4-mini',
30665
28837
  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.',
30666
28838
  pricing: {
30667
- prompt: pricing(`$4.00 / 1M tokens`),
30668
- output: pricing(`$16.00 / 1M tokens`),
28839
+ prompt: pricing(`$1.10 / 1M tokens`),
28840
+ output: pricing(`$4.40 / 1M tokens`),
30669
28841
  },
30670
28842
  },
30671
28843
  /**/
@@ -31023,8 +29195,8 @@ const OPENAI_MODELS = exportJson({
31023
29195
  modelName: 'gpt-4o-2024-05-13',
31024
29196
  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.',
31025
29197
  pricing: {
31026
- prompt: pricing(`$5.00 / 1M tokens`),
31027
- output: pricing(`$15.00 / 1M tokens`),
29198
+ prompt: pricing(`$2.50 / 1M tokens`),
29199
+ output: pricing(`$10.00 / 1M tokens`),
31028
29200
  },
31029
29201
  },
31030
29202
  /**/
@@ -31035,8 +29207,8 @@ const OPENAI_MODELS = exportJson({
31035
29207
  modelName: 'gpt-4o',
31036
29208
  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.",
31037
29209
  pricing: {
31038
- prompt: pricing(`$5.00 / 1M tokens`),
31039
- output: pricing(`$15.00 / 1M tokens`),
29210
+ prompt: pricing(`$2.50 / 1M tokens`),
29211
+ output: pricing(`$10.00 / 1M tokens`),
31040
29212
  },
31041
29213
  },
31042
29214
  /**/
@@ -31107,8 +29279,8 @@ const OPENAI_MODELS = exportJson({
31107
29279
  modelName: 'o3-mini',
31108
29280
  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.',
31109
29281
  pricing: {
31110
- prompt: pricing(`$3.00 / 1M tokens`),
31111
- output: pricing(`$12.00 / 1M tokens`),
29282
+ prompt: pricing(`$1.10 / 1M tokens`),
29283
+ output: pricing(`$4.40 / 1M tokens`),
31112
29284
  },
31113
29285
  },
31114
29286
  /**/
@@ -31208,53 +29380,6 @@ resultContent, rawResponse, duration = ZERO_VALUE) {
31208
29380
  * TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
31209
29381
  */
31210
29382
 
31211
- /**
31212
- * Maps Promptbook tools to OpenAI tools.
31213
- *
31214
- * @private
31215
- */
31216
- function mapToolsToOpenAi(tools) {
31217
- return tools.map((tool) => ({
31218
- type: 'function',
31219
- function: {
31220
- name: tool.name,
31221
- description: tool.description,
31222
- parameters: tool.parameters,
31223
- },
31224
- }));
31225
- }
31226
-
31227
- /**
31228
- * Builds a tool invocation script that injects hidden runtime context into tool args.
31229
- *
31230
- * @private utility of OpenAI tool execution wrappers
31231
- */
31232
- function buildToolInvocationScript(options) {
31233
- const { functionName, functionArgsExpression } = options;
31234
- return `
31235
- const args = ${functionArgsExpression};
31236
- const runtimeContextRaw =
31237
- typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
31238
- ? undefined
31239
- : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
31240
-
31241
- if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
31242
- args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
31243
- }
31244
-
31245
- const toolProgressTokenRaw =
31246
- typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
31247
- ? undefined
31248
- : ${TOOL_PROGRESS_TOKEN_PARAMETER};
31249
-
31250
- if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
31251
- args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
31252
- }
31253
-
31254
- return await ${functionName}(args);
31255
- `;
31256
- }
31257
-
31258
29383
  /**
31259
29384
  * Parses an OpenAI error message to identify which parameter is unsupported
31260
29385
  *
@@ -31311,6 +29436,53 @@ function isUnsupportedParameterError(error) {
31311
29436
  errorMessage.includes('does not support'));
31312
29437
  }
31313
29438
 
29439
+ /**
29440
+ * Builds a tool invocation script that injects hidden runtime context into tool args.
29441
+ *
29442
+ * @private utility of OpenAI tool execution wrappers
29443
+ */
29444
+ function buildToolInvocationScript(options) {
29445
+ const { functionName, functionArgsExpression } = options;
29446
+ return `
29447
+ const args = ${functionArgsExpression};
29448
+ const runtimeContextRaw =
29449
+ typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
29450
+ ? undefined
29451
+ : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
29452
+
29453
+ if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
29454
+ args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
29455
+ }
29456
+
29457
+ const toolProgressTokenRaw =
29458
+ typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
29459
+ ? undefined
29460
+ : ${TOOL_PROGRESS_TOKEN_PARAMETER};
29461
+
29462
+ if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
29463
+ args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
29464
+ }
29465
+
29466
+ return await ${functionName}(args);
29467
+ `;
29468
+ }
29469
+
29470
+ /**
29471
+ * Maps Promptbook tools to OpenAI tools.
29472
+ *
29473
+ * @private
29474
+ */
29475
+ function mapToolsToOpenAi(tools) {
29476
+ return tools.map((tool) => ({
29477
+ type: 'function',
29478
+ function: {
29479
+ name: tool.name,
29480
+ description: tool.description,
29481
+ parameters: tool.parameters,
29482
+ },
29483
+ }));
29484
+ }
29485
+
31314
29486
  /**
31315
29487
  * Provides access to the structured clone implementation when available.
31316
29488
  */
@@ -32277,7 +30449,7 @@ class OpenAiCompatibleExecutionTools {
32277
30449
  // Note: Match exact or prefix for model families
32278
30450
  const model = this.HARDCODED_MODELS.find(({ modelName }) => modelName === defaultModelName || modelName.startsWith(defaultModelName));
32279
30451
  if (model === undefined) {
32280
- throw new PipelineExecutionError(spaceTrim$2((block) => `
30452
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
32281
30453
  Cannot find model in ${this.title} models with name "${defaultModelName}" which should be used as default.
32282
30454
 
32283
30455
  Available models:
@@ -33203,7 +31375,7 @@ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
33203
31375
  }
33204
31376
  }
33205
31377
 
33206
- const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5-mini-2025-08-07';
31378
+ const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-nano';
33207
31379
  /**
33208
31380
  * Creates one structured log entry for streamed tool-call updates.
33209
31381
  *
@@ -33698,7 +31870,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
33698
31870
  }),
33699
31871
  ],
33700
31872
  };
33701
- const errorMessage = spaceTrim$2((block) => `
31873
+ const errorMessage = spaceTrim$1((block) => `
33702
31874
 
33703
31875
  The invoked tool \`${functionName}\` failed with error:
33704
31876
 
@@ -34416,7 +32588,7 @@ class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
34416
32588
  assertsError(error);
34417
32589
  const serializedError = serializeError(error);
34418
32590
  errors = [serializedError];
34419
- functionResponse = spaceTrim$2((block) => `
32591
+ functionResponse = spaceTrim$1((block) => `
34420
32592
 
34421
32593
  The invoked tool \`${functionName}\` failed with error:
34422
32594
 
@@ -35424,7 +33596,7 @@ class SelfLearningManager {
35424
33596
  if (isJsonSchemaResponseFormat(responseFormat)) {
35425
33597
  const jsonSchema = responseFormat.json_schema;
35426
33598
  const schemaJson = JSON.stringify(jsonSchema, null, 4);
35427
- userMessageContent = spaceTrim$2((block) => `
33599
+ userMessageContent = spaceTrim$1((block) => `
35428
33600
  ${block(prompt.content)}
35429
33601
 
35430
33602
  NOTE Request was made through OpenAI Compatible API with \`response_format\` of type \`json_schema\` with the following schema:
@@ -35455,12 +33627,12 @@ class SelfLearningManager {
35455
33627
  const formattedAgentMessage = formatAgentMessageForJsonMode(result.content, usesJsonSchemaMode);
35456
33628
  const teacherInstructions = extractOpenTeacherInstructions(agentSource);
35457
33629
  const teacherInstructionsSection = teacherInstructions
35458
- ? spaceTrim$2((block) => `
33630
+ ? spaceTrim$1((block) => `
35459
33631
  **Teacher instructions:**
35460
33632
  ${block(teacherInstructions)}
35461
33633
  `)
35462
33634
  : '';
35463
- const teacherPromptContent = spaceTrim$2((block) => `
33635
+ const teacherPromptContent = spaceTrim$1((block) => `
35464
33636
 
35465
33637
  You are a teacher agent helping another agent to learn from its interactions.
35466
33638
 
@@ -35493,7 +33665,7 @@ class SelfLearningManager {
35493
33665
  ? '- This interaction used JSON mode, so the agent answer should stay as a formatted JSON code block.'
35494
33666
  : ''}
35495
33667
  ${block(isInitialMessageMissing
35496
- ? spaceTrim$2(`
33668
+ ? spaceTrim$1(`
35497
33669
  - The agent source does not have an INITIAL MESSAGE defined, generate one.
35498
33670
  - 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.
35499
33671
  - The quick option looks like \`[👋 Hello](?message=Hello, how are you?)\`
@@ -35536,7 +33708,7 @@ class SelfLearningManager {
35536
33708
  */
35537
33709
  appendToAgentSource(section) {
35538
33710
  const currentSource = this.options.getAgentSource();
35539
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + section));
33711
+ const newSource = padBook(validateBook(spaceTrim$1(currentSource) + section));
35540
33712
  this.options.updateAgentSource(newSource);
35541
33713
  }
35542
33714
  }
@@ -35564,13 +33736,13 @@ function formatAgentMessageForJsonMode(content, isJsonMode) {
35564
33736
  }
35565
33737
  const parsedJson = tryParseJson(content);
35566
33738
  if (parsedJson === null) {
35567
- return spaceTrim$2((block) => `
33739
+ return spaceTrim$1((block) => `
35568
33740
  \`\`\`json
35569
33741
  ${block(content)}
35570
33742
  \`\`\`
35571
33743
  `);
35572
33744
  }
35573
- return spaceTrim$2((block) => `
33745
+ return spaceTrim$1((block) => `
35574
33746
  \`\`\`json
35575
33747
  ${block(JSON.stringify(parsedJson, null, 4))}
35576
33748
  \`\`\`
@@ -35602,7 +33774,7 @@ function formatSelfLearningSample(options) {
35602
33774
  const internalMessagesSection = options.internalMessages
35603
33775
  .map((internalMessage) => formatInternalLearningMessage(internalMessage))
35604
33776
  .join('\n\n');
35605
- return spaceTrim$2((block) => `
33777
+ return spaceTrim$1((block) => `
35606
33778
 
35607
33779
  USER MESSAGE
35608
33780
  ${block(options.userMessageContent)}
@@ -35620,7 +33792,7 @@ function formatSelfLearningSample(options) {
35620
33792
  * @private function of Agent
35621
33793
  */
35622
33794
  function formatInternalLearningMessage(internalMessage) {
35623
- return spaceTrim$2((block) => `
33795
+ return spaceTrim$1((block) => `
35624
33796
  INTERNAL MESSAGE
35625
33797
  ${block(stringifyInternalLearningPayload(internalMessage))}
35626
33798
  `);
@@ -36086,7 +34258,7 @@ function buildRemoteAgentSource(profile, meta) {
36086
34258
  .filter((line) => Boolean(line))
36087
34259
  .join('\n');
36088
34260
  const personaBlock = profile.personaDescription
36089
- ? spaceTrim$2((block) => `
34261
+ ? spaceTrim$1((block) => `
36090
34262
  PERSONA
36091
34263
  ${block(profile.personaDescription || '')}
36092
34264
  `)
@@ -36122,7 +34294,7 @@ class RemoteAgent extends Agent {
36122
34294
  // <- TODO: [🐱‍🚀] What about closed-source agents?
36123
34295
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
36124
34296
  if (!profileResponse.ok) {
36125
- throw new Error(spaceTrim$2((block) => `
34297
+ throw new Error(spaceTrim$1((block) => `
36126
34298
  Failed to fetch remote agent profile:
36127
34299
 
36128
34300
  Agent URL: