@promptbook/remote-server 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 +553 -2381
  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 +627 -2452
  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,7 +1,7 @@
1
1
  import { join, basename, dirname, isAbsolute } from 'path';
2
2
  import { spawn } from 'child_process';
3
3
  import colors from 'colors';
4
- import spaceTrim$2, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
+ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
5
5
  import { forTime } from 'waitasecond';
6
6
  import express from 'express';
7
7
  import * as OpenApiValidator from 'express-openapi-validator';
@@ -9,15 +9,12 @@ import http from 'http';
9
9
  import { Server } from 'socket.io';
10
10
  import swaggerUi from 'swagger-ui-express';
11
11
  import { stat, access, constants, readFile, writeFile, readdir, mkdir, watch, rm } from 'fs/promises';
12
- import { randomBytes, randomUUID } from 'crypto';
12
+ import { randomBytes } from 'crypto';
13
13
  import { SHA256 } from 'crypto-js';
14
14
  import hexEncoder from 'crypto-js/enc-hex';
15
15
  import { Readability } from '@mozilla/readability';
16
16
  import { JSDOM } from 'jsdom';
17
17
  import { Converter } from 'showdown';
18
- import { tmpdir } from 'os';
19
- import { ConfigChecker } from 'configchecker';
20
- import { chromium } from 'playwright';
21
18
  import { createElement } from 'react';
22
19
  import { renderToStaticMarkup } from 'react-dom/server';
23
20
  import { Subject, BehaviorSubject } from 'rxjs';
@@ -43,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
43
40
  * @generated
44
41
  * @see https://github.com/webgptorg/promptbook
45
42
  */
46
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-12';
43
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-15';
47
44
  /**
48
45
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
49
46
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1432,7 +1429,7 @@ async function startAgentServer(options) {
1432
1429
  function getErrorReportUrl(error) {
1433
1430
  const report = {
1434
1431
  title: `🐜 Error report from ${NAME}`,
1435
- body: spaceTrim$2((block) => `
1432
+ body: spaceTrim$1((block) => `
1436
1433
 
1437
1434
 
1438
1435
  \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
@@ -1899,7 +1896,7 @@ function serializeError(error) {
1899
1896
  const { name, message, stack } = error;
1900
1897
  const { id } = error;
1901
1898
  if (!Object.keys(ALL_ERRORS).includes(name)) {
1902
- console.error(spaceTrim$2((block) => `
1899
+ console.error(spaceTrim$1((block) => `
1903
1900
 
1904
1901
  Cannot serialize error with name "${name}"
1905
1902
 
@@ -2245,7 +2242,7 @@ function checkSerializableAsJson(options) {
2245
2242
  }
2246
2243
  else if (typeof value === 'object') {
2247
2244
  if (value instanceof Date) {
2248
- throw new UnexpectedError(spaceTrim$2((block) => `
2245
+ throw new UnexpectedError(spaceTrim$1((block) => `
2249
2246
  \`${name}\` is Date
2250
2247
 
2251
2248
  Use \`string_date_iso8601\` instead
@@ -2264,7 +2261,7 @@ function checkSerializableAsJson(options) {
2264
2261
  throw new UnexpectedError(`${name} is RegExp`);
2265
2262
  }
2266
2263
  else if (value instanceof Error) {
2267
- throw new UnexpectedError(spaceTrim$2((block) => `
2264
+ throw new UnexpectedError(spaceTrim$1((block) => `
2268
2265
  \`${name}\` is unserialized Error
2269
2266
 
2270
2267
  Use function \`serializeError\`
@@ -2287,7 +2284,7 @@ function checkSerializableAsJson(options) {
2287
2284
  }
2288
2285
  catch (error) {
2289
2286
  assertsError(error);
2290
- throw new UnexpectedError(spaceTrim$2((block) => `
2287
+ throw new UnexpectedError(spaceTrim$1((block) => `
2291
2288
  \`${name}\` is not serializable
2292
2289
 
2293
2290
  ${block(error.stack || error.message)}
@@ -2319,7 +2316,7 @@ function checkSerializableAsJson(options) {
2319
2316
  }
2320
2317
  }
2321
2318
  else {
2322
- throw new UnexpectedError(spaceTrim$2((block) => `
2319
+ throw new UnexpectedError(spaceTrim$1((block) => `
2323
2320
  \`${name}\` is unknown type
2324
2321
 
2325
2322
  Additional message for \`${name}\`:
@@ -2982,7 +2979,7 @@ function jsonParse(value) {
2982
2979
  }
2983
2980
  else if (typeof value !== 'string') {
2984
2981
  console.error('Can not parse JSON from non-string value.', { text: value });
2985
- throw new Error(spaceTrim$2(`
2982
+ throw new Error(spaceTrim$1(`
2986
2983
  Can not parse JSON from non-string value.
2987
2984
 
2988
2985
  The value type: ${typeof value}
@@ -2996,7 +2993,7 @@ function jsonParse(value) {
2996
2993
  if (!(error instanceof Error)) {
2997
2994
  throw error;
2998
2995
  }
2999
- throw new Error(spaceTrim$2((block) => `
2996
+ throw new Error(spaceTrim$1((block) => `
3000
2997
  ${block(error.message)}
3001
2998
 
3002
2999
  The expected JSON text:
@@ -3049,7 +3046,7 @@ function deserializeError(error, isStackAddedToMessage = true) {
3049
3046
  message = `${name}: ${message}`;
3050
3047
  }
3051
3048
  if (isStackAddedToMessage && stack !== undefined && stack !== '') {
3052
- message = spaceTrim$2((block) => `
3049
+ message = spaceTrim$1((block) => `
3053
3050
  ${block(message)}
3054
3051
 
3055
3052
  Original stack trace:
@@ -3604,7 +3601,7 @@ function pipelineJsonToString(pipelineJson) {
3604
3601
  pipelineString += '\n\n';
3605
3602
  pipelineString += '```' + contentLanguage;
3606
3603
  pipelineString += '\n';
3607
- pipelineString += spaceTrim$2(content);
3604
+ pipelineString += spaceTrim$1(content);
3608
3605
  // <- TODO: [main] !!3 Escape
3609
3606
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
3610
3607
  pipelineString += '\n';
@@ -4036,14 +4033,14 @@ class MultipleLlmExecutionTools {
4036
4033
  if (description === undefined) {
4037
4034
  return headLine;
4038
4035
  }
4039
- return spaceTrim$2((block) => `
4036
+ return spaceTrim$1((block) => `
4040
4037
  ${headLine}
4041
4038
 
4042
4039
  ${ /* <- Note: Indenting the description: */block(description)}
4043
4040
  `);
4044
4041
  })
4045
4042
  .join('\n\n');
4046
- return spaceTrim$2((block) => `
4043
+ return spaceTrim$1((block) => `
4047
4044
  Multiple LLM Providers:
4048
4045
 
4049
4046
  ${block(innerModelsTitlesAndDescriptions)}
@@ -4145,7 +4142,7 @@ class MultipleLlmExecutionTools {
4145
4142
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
4146
4143
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
4147
4144
  // 3) ...
4148
- spaceTrim$2((block) => `
4145
+ spaceTrim$1((block) => `
4149
4146
  All execution tools of ${this.title} failed:
4150
4147
 
4151
4148
  ${block(errors
@@ -4158,7 +4155,7 @@ class MultipleLlmExecutionTools {
4158
4155
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\` into ${this.title}`);
4159
4156
  }
4160
4157
  else {
4161
- throw new PipelineExecutionError(spaceTrim$2((block) => `
4158
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4162
4159
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}" into ${this.title}
4163
4160
 
4164
4161
  Available \`LlmExecutionTools\`:
@@ -4195,7 +4192,7 @@ class MultipleLlmExecutionTools {
4195
4192
  */
4196
4193
  function joinLlmExecutionTools(title, ...llmExecutionTools) {
4197
4194
  if (llmExecutionTools.length === 0) {
4198
- const warningMessage = spaceTrim$2(`
4195
+ const warningMessage = spaceTrim$1(`
4199
4196
  You have not provided any \`LlmExecutionTools\`
4200
4197
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
4201
4198
 
@@ -4512,14 +4509,14 @@ function $registeredScrapersMessage(availableScrapers) {
4512
4509
  return { ...metadata, isMetadataAviailable, isInstalled, isAvailableInTools };
4513
4510
  });
4514
4511
  if (metadata.length === 0) {
4515
- return spaceTrim$2(`
4512
+ return spaceTrim$1(`
4516
4513
  **No scrapers are available**
4517
4514
 
4518
4515
  This is a unexpected behavior, you are probably using some broken version of Promptbook
4519
4516
  At least there should be available the metadata of the scrapers
4520
4517
  `);
4521
4518
  }
4522
- return spaceTrim$2((block) => `
4519
+ return spaceTrim$1((block) => `
4523
4520
  Available scrapers are:
4524
4521
  ${block(metadata
4525
4522
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvailableInBrowser, isAvailableInTools, }, i) => {
@@ -5025,7 +5022,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
5025
5022
  else if (urlOrRequest instanceof Request) {
5026
5023
  url = urlOrRequest.url;
5027
5024
  }
5028
- throw new PromptbookFetchError(spaceTrim$2((block) => `
5025
+ throw new PromptbookFetchError(spaceTrim$1((block) => `
5029
5026
  Can not fetch "${url}"
5030
5027
 
5031
5028
  Fetch error:
@@ -5185,7 +5182,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5185
5182
  const fileExtension = getFileExtension(filename);
5186
5183
  const mimeType = extensionToMimeType(fileExtension || '');
5187
5184
  if (!(await isFileExisting(filename, tools.fs))) {
5188
- throw new NotFoundError(spaceTrim$2((block) => `
5185
+ throw new NotFoundError(spaceTrim$1((block) => `
5189
5186
  Can not make source handler for file which does not exist:
5190
5187
 
5191
5188
  File:
@@ -5278,7 +5275,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
5278
5275
  // <- TODO: [🪓] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
5279
5276
  break;
5280
5277
  }
5281
- console.warn(spaceTrim$2((block) => `
5278
+ console.warn(spaceTrim$1((block) => `
5282
5279
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
5283
5280
 
5284
5281
  The source:
@@ -5294,7 +5291,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
5294
5291
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
5295
5292
  }
5296
5293
  if (partialPieces === null) {
5297
- throw new KnowledgeScrapeError(spaceTrim$2((block) => `
5294
+ throw new KnowledgeScrapeError(spaceTrim$1((block) => `
5298
5295
  Cannot scrape knowledge
5299
5296
 
5300
5297
  The source:
@@ -5869,7 +5866,7 @@ const CsvFormatParser = {
5869
5866
  const { value, outputParameterName, settings, mapCallback, onProgress } = options;
5870
5867
  const csv = csvParse(value, settings);
5871
5868
  if (csv.errors.length !== 0) {
5872
- throw new CsvFormatError(spaceTrim$2((block) => `
5869
+ throw new CsvFormatError(spaceTrim$1((block) => `
5873
5870
  CSV parsing error
5874
5871
 
5875
5872
  Error(s) from CSV parsing:
@@ -5914,7 +5911,7 @@ const CsvFormatParser = {
5914
5911
  const { value, settings, mapCallback, onProgress } = options;
5915
5912
  const csv = csvParse(value, settings);
5916
5913
  if (csv.errors.length !== 0) {
5917
- throw new CsvFormatError(spaceTrim$2((block) => `
5914
+ throw new CsvFormatError(spaceTrim$1((block) => `
5918
5915
  CSV parsing error
5919
5916
 
5920
5917
  Error(s) from CSV parsing:
@@ -6124,7 +6121,7 @@ function mapAvailableToExpectedParameters(options) {
6124
6121
  }
6125
6122
  // Phase 2️⃣: Non-matching mapping
6126
6123
  if (expectedParameterNames.size !== availableParametersNames.size) {
6127
- throw new PipelineExecutionError(spaceTrim$2((block) => `
6124
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
6128
6125
  Can not map available parameters to expected parameters
6129
6126
 
6130
6127
  Mapped parameters:
@@ -6927,7 +6924,7 @@ async function executeFormatSubvalues(options) {
6927
6924
  return /* not await */ executeAttempts({ ...options, logLlmCall });
6928
6925
  }
6929
6926
  if (jokerParameterNames.length !== 0) {
6930
- throw new UnexpectedError(spaceTrim$2((block) => `
6927
+ throw new UnexpectedError(spaceTrim$1((block) => `
6931
6928
  JOKER parameters are not supported together with FOREACH command
6932
6929
 
6933
6930
  [🧞‍♀️] This should be prevented in \`validatePipeline\`
@@ -6940,7 +6937,7 @@ async function executeFormatSubvalues(options) {
6940
6937
  if (formatDefinition === undefined) {
6941
6938
  throw new UnexpectedError(
6942
6939
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
6943
- spaceTrim$2((block) => `
6940
+ spaceTrim$1((block) => `
6944
6941
  Unsupported format "${task.foreach.formatName}"
6945
6942
 
6946
6943
  Available formats:
@@ -6957,7 +6954,7 @@ async function executeFormatSubvalues(options) {
6957
6954
  if (subvalueParser === undefined) {
6958
6955
  throw new UnexpectedError(
6959
6956
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
6960
- spaceTrim$2((block) => `
6957
+ spaceTrim$1((block) => `
6961
6958
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
6962
6959
 
6963
6960
  Available subformat names for format "${formatDefinition.formatName}":
@@ -6997,7 +6994,7 @@ async function executeFormatSubvalues(options) {
6997
6994
  if (!(error instanceof PipelineExecutionError)) {
6998
6995
  throw error;
6999
6996
  }
7000
- const highLevelError = new PipelineExecutionError(spaceTrim$2((block) => `
6997
+ const highLevelError = new PipelineExecutionError(spaceTrim$1((block) => `
7001
6998
  ${error.message}
7002
6999
 
7003
7000
  This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -7021,7 +7018,7 @@ async function executeFormatSubvalues(options) {
7021
7018
  ...options,
7022
7019
  priority: priority + index,
7023
7020
  parameters: allSubparameters,
7024
- pipelineIdentification: spaceTrim$2((block) => `
7021
+ pipelineIdentification: spaceTrim$1((block) => `
7025
7022
  ${block(pipelineIdentification)}
7026
7023
  Subparameter index: ${index}
7027
7024
  `),
@@ -7030,7 +7027,7 @@ async function executeFormatSubvalues(options) {
7030
7027
  }
7031
7028
  catch (error) {
7032
7029
  if (length > BIG_DATASET_TRESHOLD) {
7033
- console.error(spaceTrim$2((block) => `
7030
+ console.error(spaceTrim$1((block) => `
7034
7031
  ${error.message}
7035
7032
 
7036
7033
  This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
@@ -7920,13 +7917,13 @@ function $registeredLlmToolsMessage() {
7920
7917
  });
7921
7918
  const usedEnvMessage = `Unknown \`.env\` file` ;
7922
7919
  if (metadata.length === 0) {
7923
- return spaceTrim$2((block) => `
7920
+ return spaceTrim$1((block) => `
7924
7921
  No LLM providers are available.
7925
7922
 
7926
7923
  ${block(usedEnvMessage)}
7927
7924
  `);
7928
7925
  }
7929
- return spaceTrim$2((block) => `
7926
+ return spaceTrim$1((block) => `
7930
7927
 
7931
7928
  ${block(usedEnvMessage)}
7932
7929
 
@@ -7972,7 +7969,7 @@ function $registeredLlmToolsMessage() {
7972
7969
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
7973
7970
  }
7974
7971
  }
7975
- let providerMessage = spaceTrim$2(`
7972
+ let providerMessage = spaceTrim$1(`
7976
7973
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
7977
7974
  ${morePieces.join('; ')}
7978
7975
  `);
@@ -8018,7 +8015,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
8018
8015
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
8019
8016
  if (registeredItem === undefined) {
8020
8017
  // console.log('$llmToolsRegister.list()', $llmToolsRegister.list());
8021
- throw new Error(spaceTrim$2((block) => `
8018
+ throw new Error(spaceTrim$1((block) => `
8022
8019
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
8023
8020
  Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
8024
8021
 
@@ -8397,7 +8394,7 @@ function buildParametersSection(items) {
8397
8394
  const entries = items
8398
8395
  .flatMap((item) => formatParameterListItem(item).split(/\r?\n/))
8399
8396
  .filter((line) => line !== '');
8400
- return spaceTrim$2((block) => `
8397
+ return spaceTrim$1((block) => `
8401
8398
  **Parameters:**
8402
8399
  ${block(entries.join('\n'))}
8403
8400
 
@@ -8470,7 +8467,7 @@ function isPromptString(value) {
8470
8467
  */
8471
8468
  function prompt(strings, ...values) {
8472
8469
  if (values.length === 0) {
8473
- return new PromptString(spaceTrim$2(strings.join('')));
8470
+ return new PromptString(spaceTrim$1(strings.join('')));
8474
8471
  }
8475
8472
  const stringsWithHiddenParameters = strings.map((stringsItem) => ParameterEscaping.hideBrackets(stringsItem));
8476
8473
  const parameterMetadata = values.map((value) => {
@@ -8511,7 +8508,7 @@ function prompt(strings, ...values) {
8511
8508
  ? `${result}${stringsItem}`
8512
8509
  : `${result}${stringsItem}${ParameterSection.formatParameterPlaceholder(parameterName)}`;
8513
8510
  }, '');
8514
- pipelineString = spaceTrim$2(pipelineString);
8511
+ pipelineString = spaceTrim$1(pipelineString);
8515
8512
  try {
8516
8513
  pipelineString = templateParameters(pipelineString, parameters);
8517
8514
  }
@@ -8520,7 +8517,7 @@ function prompt(strings, ...values) {
8520
8517
  throw error;
8521
8518
  }
8522
8519
  console.error({ pipelineString, parameters, parameterNames: parameterNamesOrdered, error });
8523
- throw new UnexpectedError(spaceTrim$2((block) => `
8520
+ throw new UnexpectedError(spaceTrim$1((block) => `
8524
8521
  Internal error in prompt template literal
8525
8522
 
8526
8523
  ${block(JSON.stringify({ strings, values }, null, 4))}}
@@ -8601,7 +8598,7 @@ function attachClientVersionHeader(headers) {
8601
8598
  * @public exported from `@promptbook/utils`
8602
8599
  */
8603
8600
  function computeHash(value) {
8604
- return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
8601
+ return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
8605
8602
  }
8606
8603
  /**
8607
8604
  * TODO: [🥬][🥬] Use this ACRY
@@ -10510,8 +10507,8 @@ class MarkdownScraper {
10510
10507
  knowledgeTextPieces.map(async (knowledgeTextPiece, i) => {
10511
10508
  // Note: These are just default values, they will be overwritten by the actual values:
10512
10509
  let name = `piece-${i}`;
10513
- let title = spaceTrim$2(knowledgeTextPiece.substring(0, 100));
10514
- const knowledgePieceContent = spaceTrim$2(knowledgeTextPiece);
10510
+ let title = spaceTrim$1(knowledgeTextPiece.substring(0, 100));
10511
+ const knowledgePieceContent = spaceTrim$1(knowledgeTextPiece);
10515
10512
  let keywords = [];
10516
10513
  const index = [];
10517
10514
  /*
@@ -10524,7 +10521,7 @@ class MarkdownScraper {
10524
10521
  isCrashedOnError: true,
10525
10522
  });
10526
10523
  const { title: titleRaw = 'Untitled' } = titleResult.outputParameters;
10527
- title = spaceTrim$2(titleRaw) /* <- TODO: Maybe do in pipeline */;
10524
+ title = spaceTrim$1(titleRaw) /* <- TODO: Maybe do in pipeline */;
10528
10525
  name = titleToName(title);
10529
10526
  // --- Keywords
10530
10527
  const keywordsResult = await prepareKeywordsExecutor({ knowledgePieceContent }).asPromise({
@@ -10877,2200 +10874,190 @@ async function fetchUrlContent(url) {
10877
10874
  */
10878
10875
 
10879
10876
  /**
10880
- * Prompt parameter key used to pass hidden runtime context to tool execution.
10881
- *
10882
- * @private internal runtime wiring for commitment tools
10883
- */
10884
- const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
10885
- /**
10886
- * Hidden argument key used to pass runtime context into individual tool calls.
10887
- *
10888
- * @private internal runtime wiring for commitment tools
10889
- */
10890
- const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
10891
- /**
10892
- * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
10893
- *
10894
- * @private internal runtime wiring for commitment tools
10895
- */
10896
- const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
10897
- /**
10898
- * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
10899
- *
10900
- * @private internal runtime wiring for commitment tools
10901
- */
10902
- const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
10903
- /**
10904
- * Monotonic counter used for hidden progress-listener tokens.
10877
+ * Cached implementation of `run_browser` when it can be resolved.
10905
10878
  *
10906
- * @private internal runtime wiring for commitment tools
10879
+ * @private internal utility for USE BROWSER commitment
10907
10880
  */
10908
- let toolCallProgressListenerCounter = 0;
10881
+ let cachedRunBrowserTool = null;
10909
10882
  /**
10910
- * Active tool-progress listeners keyed by hidden execution token.
10883
+ * Cached loading error to avoid repeating expensive resolution attempts.
10911
10884
  *
10912
- * @private internal runtime wiring for commitment tools
10885
+ * @private internal utility for USE BROWSER commitment
10913
10886
  */
10914
- const toolCallProgressListeners = new Map();
10887
+ let cachedRunBrowserToolError = null;
10915
10888
  /**
10916
- * Parses unknown runtime context payload into a normalized object.
10889
+ * Attempts to load the server-side `run_browser` tool lazily.
10917
10890
  *
10918
- * @private internal runtime wiring for commitment tools
10891
+ * @returns Loaded `run_browser` implementation
10892
+ * @private internal utility for USE BROWSER commitment
10919
10893
  */
10920
- function parseToolRuntimeContext(rawValue) {
10921
- if (!rawValue) {
10922
- return null;
10894
+ function loadRunBrowserToolForNode() {
10895
+ if (cachedRunBrowserTool !== null) {
10896
+ return cachedRunBrowserTool;
10923
10897
  }
10924
- let parsed = rawValue;
10925
- if (typeof rawValue === 'string') {
10926
- try {
10927
- parsed = JSON.parse(rawValue);
10928
- }
10929
- catch (_a) {
10930
- return null;
10898
+ if (cachedRunBrowserToolError !== null) {
10899
+ throw cachedRunBrowserToolError;
10900
+ }
10901
+ try {
10902
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
10903
+ const runBrowserModule = require('../../../apps/agents-server/src/tools/run_browser');
10904
+ if (typeof runBrowserModule.run_browser !== 'function') {
10905
+ throw new Error('run_browser value is not a function but ' + typeof runBrowserModule.run_browser);
10931
10906
  }
10907
+ cachedRunBrowserTool = runBrowserModule.run_browser;
10908
+ return cachedRunBrowserTool;
10932
10909
  }
10933
- if (!parsed || typeof parsed !== 'object') {
10934
- return null;
10910
+ catch (error) {
10911
+ assertsError(error);
10912
+ cachedRunBrowserToolError = error;
10913
+ throw error;
10935
10914
  }
10936
- return parsed;
10937
- }
10938
- /**
10939
- * Reads runtime context attached to tool call arguments.
10940
- *
10941
- * @private internal runtime wiring for commitment tools
10942
- */
10943
- function readToolRuntimeContextFromToolArgs(args) {
10944
- return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
10945
10915
  }
10946
10916
  /**
10947
- * Reads the hidden tool-progress token from tool arguments.
10917
+ * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
10948
10918
  *
10949
- * @private internal runtime wiring for commitment tools
10950
- */
10951
- function readToolProgressTokenFromToolArgs(args) {
10952
- const token = args[TOOL_PROGRESS_TOKEN_ARGUMENT];
10953
- return typeof token === 'string' && token.trim().length > 0 ? token : null;
10954
- }
10955
- /**
10956
- * Serializes runtime context for prompt parameters.
10919
+ * This uses fully lazy resolution to keep CLI startup independent from optional browser tooling.
10920
+ * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
10957
10921
  *
10958
- * @private internal runtime wiring for commitment tools
10922
+ * @private internal utility for USE BROWSER commitment
10959
10923
  */
10960
- function serializeToolRuntimeContext(context) {
10961
- return JSON.stringify(context);
10924
+ function resolveRunBrowserToolForNode() {
10925
+ return async (args) => {
10926
+ try {
10927
+ const runBrowserTool = loadRunBrowserToolForNode();
10928
+ return await runBrowserTool(args);
10929
+ }
10930
+ catch (error) {
10931
+ assertsError(error);
10932
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
10933
+ \`run_browser\` tool is not available in this environment.
10934
+ This commitment requires the Agents Server browser runtime with Playwright CLI.
10935
+
10936
+ ${error.name}:
10937
+ ${block(error.message)}
10938
+ `));
10939
+ }
10940
+ };
10962
10941
  }
10942
+
10963
10943
  /**
10964
- * Registers one in-memory listener that receives progress updates emitted by a running tool.
10944
+ * Resolves the server-side implementation of the send_email tool for Node.js environments.
10965
10945
  *
10966
- * The returned token is passed into script execution as a hidden argument so tool implementations
10967
- * can stream progress without exposing extra parameters to the model.
10946
+ * This uses a lazy require so the core package can still load even if the Agents Server
10947
+ * module is unavailable. When the server tool cannot be resolved, a fallback implementation
10948
+ * throws a helpful error message.
10968
10949
  *
10969
- * @param listener - Listener notified about tool progress.
10970
- * @returns Hidden token used to route progress updates.
10971
- * @private internal runtime wiring for commitment tools
10950
+ * @private internal utility for USE EMAIL commitment
10972
10951
  */
10973
- function registerToolCallProgressListener(listener) {
10974
- toolCallProgressListenerCounter += 1;
10975
- const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
10976
- toolCallProgressListeners.set(token, listener);
10977
- return token;
10952
+ function resolveSendEmailToolForNode() {
10953
+ try {
10954
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
10955
+ const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
10956
+ if (typeof send_email !== 'function') {
10957
+ throw new Error('send_email value is not a function but ' + typeof send_email);
10958
+ }
10959
+ return send_email;
10960
+ }
10961
+ catch (error) {
10962
+ const normalizedError = error instanceof Error
10963
+ ? error
10964
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
10965
+ return async () => {
10966
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
10967
+ \`send_email\` tool is not available in this environment.
10968
+ This commitment requires Agents Server runtime with wallet-backed SMTP sending.
10969
+
10970
+ ${normalizedError.name}:
10971
+ ${block(normalizedError.message)}
10972
+ `));
10973
+ };
10974
+ }
10978
10975
  }
10976
+
10979
10977
  /**
10980
- * Unregisters one in-memory progress listener.
10978
+ * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
10981
10979
  *
10982
- * @param token - Token previously created by `registerToolCallProgressListener`.
10983
- * @private internal runtime wiring for commitment tools
10984
- */
10985
- function unregisterToolCallProgressListener(token) {
10986
- toolCallProgressListeners.delete(token);
10987
- }
10988
- /**
10989
- * Emits one tool progress update using a hidden token carried in tool arguments.
10980
+ * Uses lazy require so core package can load outside Agents Server.
10990
10981
  *
10991
- * @param args - Raw tool arguments including hidden runtime keys.
10992
- * @param update - Incremental progress update.
10993
- * @returns `true` when a listener was found and notified.
10994
- * @private internal runtime wiring for commitment tools
10982
+ * @private internal utility for USE SPAWN commitment
10995
10983
  */
10996
- function emitToolCallProgressFromToolArgs(args, update) {
10997
- const token = readToolProgressTokenFromToolArgs(args);
10998
- if (!token) {
10999
- return false;
10984
+ function resolveSpawnAgentToolForNode() {
10985
+ try {
10986
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
10987
+ const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
10988
+ if (typeof spawn_agent !== 'function') {
10989
+ throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
10990
+ }
10991
+ return spawn_agent;
11000
10992
  }
11001
- const listener = toolCallProgressListeners.get(token);
11002
- if (!listener) {
11003
- return false;
10993
+ catch (error) {
10994
+ const normalizedError = error instanceof Error
10995
+ ? error
10996
+ : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
10997
+ return async () => {
10998
+ throw new EnvironmentMismatchError(spaceTrim$1((block) => `
10999
+ \`spawn_agent\` tool is not available in this environment.
11000
+ This commitment requires Agents Server runtime with agent persistence enabled.
11001
+
11002
+ ${normalizedError.name}:
11003
+ ${block(normalizedError.message)}
11004
+ `));
11005
+ };
11004
11006
  }
11005
- listener(update);
11006
- return true;
11007
11007
  }
11008
- /**
11009
- * Note: [💞] Ignore a discrepancy between file name and entity name
11010
- */
11011
11008
 
11012
11009
  /**
11013
- * Logical public directory marker used in `run_browser` payload paths.
11010
+ * Generates a regex pattern to match a specific commitment
11014
11011
  *
11015
- * This value is kept stable for UI parsing and `/api/browser-artifacts/*` URL mapping.
11016
- */
11017
- const RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY = '.playwright-cli';
11018
- /**
11019
- * Runtime environment variable that overrides local artifact storage directory.
11020
- */
11021
- const RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV = 'RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY';
11022
- /**
11023
- * Default writable directory for `run_browser` screenshot/video artifacts.
11024
- */
11025
- const DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY = join(tmpdir(), 'promptbook', 'run-browser-artifacts');
11026
- /**
11027
- * Converts Windows separators to POSIX separators for payload paths.
11028
- */
11029
- function toPosixPath(pathname) {
11030
- return pathname.split('\\').join('/');
11031
- }
11032
- /**
11033
- * Resolves writable filesystem directory used for artifact persistence.
11012
+ * Note: It always creates new Regex object
11013
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
11014
+ *
11015
+ * @private - TODO: [🧠] Maybe should be public?
11034
11016
  */
11035
- function resolveRunBrowserArtifactStorageDirectory() {
11036
- const configuredStorageDirectory = process.env[RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV];
11037
- if (configuredStorageDirectory && configuredStorageDirectory.trim()) {
11038
- return configuredStorageDirectory.trim();
11017
+ function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
11018
+ const allCommitments = [commitment, ...aliases];
11019
+ const patterns = allCommitments.map((commitment) => {
11020
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11021
+ return escapedCommitment.split(/\s+/).join('\\s+');
11022
+ });
11023
+ const keywordPattern = patterns.join('|');
11024
+ if (requiresContent) {
11025
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
11026
+ }
11027
+ else {
11028
+ return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
11039
11029
  }
11040
- return DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY;
11041
- }
11042
- /**
11043
- * Resolves absolute filesystem path of one artifact filename.
11044
- */
11045
- function resolveRunBrowserArtifactFilesystemPath(artifactFilename) {
11046
- return join(resolveRunBrowserArtifactStorageDirectory(), artifactFilename);
11047
11030
  }
11048
11031
  /**
11049
- * Resolves payload path of one artifact filename used by replay renderers.
11032
+ * Generates a regex pattern to match a specific commitment type
11033
+ *
11034
+ * Note: It just matches the type part of the commitment
11035
+ * Note: It always creates new Regex object
11036
+ * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
11037
+ *
11038
+ * @private
11050
11039
  */
11051
- function resolveRunBrowserArtifactPublicPath(artifactFilename) {
11052
- return toPosixPath(`${RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY}/${artifactFilename}`);
11040
+ function createCommitmentTypeRegex(commitment, aliases = []) {
11041
+ const allCommitments = [commitment, ...aliases];
11042
+ const patterns = allCommitments.map((commitment) => {
11043
+ const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11044
+ return escapedCommitment.split(/\s+/).join('\\s+');
11045
+ });
11046
+ const keywordPattern = patterns.join('|');
11047
+ const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
11048
+ return regex;
11053
11049
  }
11054
11050
 
11055
11051
  /**
11056
- * Error code used for remote-browser infrastructure outages.
11057
- */
11058
- const REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE = 'REMOTE_BROWSER_UNAVAILABLE';
11059
- /**
11060
- * Error thrown when a remote Playwright browser cannot be reached.
11052
+ * Base implementation of CommitmentDefinition that provides common functionality
11053
+ * Most commitments can extend this class and only override the applyToAgentModelRequirements method
11054
+ *
11055
+ * @private
11061
11056
  */
11062
- class RemoteBrowserUnavailableError extends KnowledgeScrapeError {
11063
- constructor(options) {
11064
- var _a;
11065
- super(options.message);
11066
- this.code = REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
11067
- this.isRetryable = true;
11068
- this.debug = options.debug;
11069
- this.suggestedNextSteps =
11070
- (_a = options.suggestedNextSteps) !== null && _a !== void 0 ? _a : [
11071
- 'Verify remote browser infrastructure is running and reachable from Agents Server.',
11072
- 'Check firewall and DNS routing for the remote browser host and port.',
11073
- 'Retry later or continue with non-graphical fallback scraping.',
11074
- ];
11075
- this.cause = options.cause;
11076
- Object.setPrototypeOf(this, RemoteBrowserUnavailableError.prototype);
11077
- }
11078
- }
11079
- /**
11080
- * Returns true when an unknown value is one of the remote-browser outage errors.
11081
- */
11082
- function isRemoteBrowserUnavailableError(error) {
11083
- return error instanceof RemoteBrowserUnavailableError;
11084
- }
11085
- /**
11086
- * Sanitizes a remote websocket endpoint so debug payloads never expose path secrets.
11087
- */
11088
- function sanitizeRemoteBrowserEndpoint(wsEndpoint) {
11089
- var _a, _b;
11090
- try {
11091
- const parsedEndpoint = new URL(wsEndpoint);
11092
- return {
11093
- protocol: parsedEndpoint.protocol || null,
11094
- host: parsedEndpoint.hostname || null,
11095
- port: parsedEndpoint.port ? Number.parseInt(parsedEndpoint.port, 10) : null,
11096
- };
11097
- }
11098
- catch (_c) {
11099
- const hostPortMatch = wsEndpoint.trim().match(/^(?:wss?:\/\/)?(?<host>[^:/?#]+)(?::(?<port>\d{1,5}))?/i);
11100
- const host = ((_a = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _a === void 0 ? void 0 : _a.host) || null;
11101
- const parsedPort = (_b = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _b === void 0 ? void 0 : _b.port;
11102
- return {
11103
- protocol: wsEndpoint.startsWith('wss://') ? 'wss:' : wsEndpoint.startsWith('ws://') ? 'ws:' : null,
11104
- host,
11105
- port: parsedPort ? Number.parseInt(parsedPort, 10) : null,
11106
- };
11107
- }
11108
- }
11109
- /**
11110
- * Extracts network-like error code from unknown error payload.
11111
- */
11112
- function extractNetworkErrorCode(error) {
11113
- var _a;
11114
- if (error && typeof error === 'object') {
11115
- const maybeCode = error.code;
11116
- if (typeof maybeCode === 'string' && maybeCode.trim()) {
11117
- return maybeCode.trim().toUpperCase();
11118
- }
11119
- }
11120
- const message = getErrorMessage(error);
11121
- const match = message.match(/\b(ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNRESET|EHOSTUNREACH|ENETUNREACH)\b/i);
11122
- return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || null;
11123
- }
11124
- /**
11125
- * Classifies whether an unknown error most likely represents remote browser infra outage.
11126
- */
11127
- function isRemoteBrowserInfrastructureError(error) {
11128
- const networkErrorCode = extractNetworkErrorCode(error);
11129
- if (networkErrorCode) {
11130
- return true;
11131
- }
11132
- const message = getErrorMessage(error).toLowerCase();
11133
- const isWebSocketFailure = message.includes('websocket') ||
11134
- message.includes('<ws error>') ||
11135
- message.includes('ws connect error') ||
11136
- message.includes('socket hang up');
11137
- const hasHandshakeFailure = message.includes('unexpected server response') ||
11138
- message.includes('handshake') ||
11139
- message.includes('code=1006') ||
11140
- message.includes('disconnected');
11141
- return isWebSocketFailure && hasHandshakeFailure;
11142
- }
11143
- /**
11144
- * Converts unknown thrown values into safe string messages.
11145
- */
11146
- function getErrorMessage(error) {
11147
- return error instanceof Error ? error.message : String(error);
11148
- }
11149
- /**
11150
- * Converts unknown errors into stack payloads that are safe to render in debug mode.
11151
- */
11152
- function getErrorStack(error) {
11153
- return error instanceof Error && error.stack ? error.stack : null;
11154
- }
11155
-
11156
- /**
11157
- * Matches unsupported characters in snapshot file suffixes.
11158
- */
11159
- const SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN = /[^a-z0-9-]/g;
11160
- /**
11161
- * Creates one filesystem-safe optional filename suffix for a snapshot.
11162
- */
11163
- function createSnapshotFileSuffix(rawSuffix) {
11164
- if (!rawSuffix) {
11165
- return '';
11166
- }
11167
- const normalized = rawSuffix
11168
- .trim()
11169
- .toLowerCase()
11170
- .replace(/\s+/g, '-')
11171
- .replace(SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN, '-')
11172
- .replace(/-+/g, '-')
11173
- .replace(/^-|-$/g, '');
11174
- return normalized;
11175
- }
11176
- /**
11177
- * Resolves snapshot filename for one session and optional stage suffix.
11178
- */
11179
- function resolveSnapshotFilename(sessionId, fileSuffix) {
11180
- const safeSuffix = createSnapshotFileSuffix(fileSuffix);
11181
- return safeSuffix ? `${sessionId}-${safeSuffix}.png` : `${sessionId}.png`;
11182
- }
11183
- /**
11184
- * Creates one user-facing description for an executed browser action.
11185
- */
11186
- function formatActionSummary(action) {
11187
- switch (action.type) {
11188
- case 'navigate':
11189
- return `Navigate to ${action.url}`;
11190
- case 'click':
11191
- return `Click ${action.selector}`;
11192
- case 'type':
11193
- return `Type into ${action.selector}`;
11194
- case 'wait':
11195
- return `Wait ${action.milliseconds}ms`;
11196
- case 'scroll':
11197
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
11198
- }
11199
- }
11200
- /**
11201
- * Screenshot/artifact and page-cleanup helpers for `run_browser`.
11202
- *
11203
- * @private function of `run_browser`
11204
- */
11205
- const runBrowserArtifacts = {
11206
- /**
11207
- * Captures a screenshot artifact for the current page and returns relative path.
11208
- */
11209
- async captureSnapshot(page, sessionId, fileSuffix) {
11210
- const snapshotFilename = resolveSnapshotFilename(sessionId, fileSuffix);
11211
- const snapshotDirectoryPath = resolveRunBrowserArtifactStorageDirectory();
11212
- const snapshotPath = resolveRunBrowserArtifactFilesystemPath(snapshotFilename);
11213
- try {
11214
- await mkdir(snapshotDirectoryPath, { recursive: true });
11215
- try {
11216
- await page.screenshot({ path: snapshotPath, fullPage: true });
11217
- }
11218
- catch (error) {
11219
- console.warn('[run_browser] Full-page snapshot failed, retrying viewport-only screenshot', {
11220
- sessionId,
11221
- snapshotFilename,
11222
- error: getErrorMessage(error),
11223
- });
11224
- await page.screenshot({ path: snapshotPath, fullPage: false });
11225
- }
11226
- return resolveRunBrowserArtifactPublicPath(snapshotFilename);
11227
- }
11228
- catch (error) {
11229
- console.error('[run_browser] Failed to capture snapshot', {
11230
- sessionId,
11231
- snapshotFilename,
11232
- error: getErrorMessage(error),
11233
- });
11234
- return null;
11235
- }
11236
- },
11237
- /**
11238
- * Safely retrieves page title from current browser page.
11239
- */
11240
- async getPageTitle(page) {
11241
- try {
11242
- return await page.title();
11243
- }
11244
- catch (_a) {
11245
- return null;
11246
- }
11247
- },
11248
- /**
11249
- * Closes browser page and logs non-fatal cleanup errors.
11250
- */
11251
- async cleanupPage(page, sessionId) {
11252
- if (!page) {
11253
- return;
11254
- }
11255
- try {
11256
- await page.close();
11257
- }
11258
- catch (error) {
11259
- console.error('[run_browser] Failed to cleanup browser page', {
11260
- sessionId,
11261
- error: getErrorMessage(error),
11262
- });
11263
- }
11264
- },
11265
- /**
11266
- * Captures one screenshot artifact and enriches it with page metadata.
11267
- */
11268
- async captureSnapshotArtifact(options) {
11269
- const { page, sessionId, label, fileSuffix, actionIndex, action } = options;
11270
- const path = await this.captureSnapshot(page, sessionId, fileSuffix);
11271
- if (!path) {
11272
- return null;
11273
- }
11274
- const actionSummary = action ? formatActionSummary(action) : undefined;
11275
- return {
11276
- kind: 'screenshot',
11277
- label,
11278
- path,
11279
- capturedAt: new Date().toISOString(),
11280
- url: page.url(),
11281
- title: await this.getPageTitle(page),
11282
- actionIndex,
11283
- actionSummary,
11284
- };
11285
- },
11286
- };
11287
-
11288
- /**
11289
- * Shared constants used by the `run_browser` tool.
11290
- *
11291
- * @private internal constants of `run_browser`
11292
- */
11293
- const runBrowserConstants = {
11294
- sessionPrefix: 'agents-server-run-browser',
11295
- snapshotDirectory: '.playwright-cli',
11296
- resultSchema: 'promptbook/run-browser@1',
11297
- defaultWaitMs: 1000,
11298
- maxWaitMs: 60000,
11299
- defaultScrollPixels: 800,
11300
- defaultNavigationTimeoutMs: 20000,
11301
- defaultActionTimeoutMs: 15000,
11302
- fallbackDynamicContentWarning: 'Remote browser is unavailable. Fallback scraping was used and dynamic content may be missing.',
11303
- validationErrorCode: 'RUN_BROWSER_VALIDATION_ERROR',
11304
- navigationFailedErrorCode: 'RUN_BROWSER_NAVIGATION_FAILED',
11305
- actionFailedErrorCode: 'RUN_BROWSER_ACTION_FAILED',
11306
- cancelledErrorCode: 'RUN_BROWSER_CANCELLED',
11307
- unknownErrorCode: 'RUN_BROWSER_UNKNOWN_ERROR',
11308
- };
11309
-
11310
- const config = ConfigChecker.from({
11311
- ...process.env,
11312
- // Note: To expose env variables to the browser, using this seemingly strange syntax:
11313
- // @see https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser
11314
- NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
11315
- NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV,
11316
- NEXT_PUBLIC_VERCEL_TARGET_ENV: process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
11317
- NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
11318
- NEXT_PUBLIC_VERCEL_BRANCH_URL: process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL,
11319
- NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL: process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL,
11320
- NEXT_PUBLIC_VERCEL_GIT_PROVIDER: process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
11321
- NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
11322
- NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
11323
- NEXT_PUBLIC_VERCEL_GIT_REPO_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_ID,
11324
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
11325
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
11326
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
11327
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME,
11328
- NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
11329
- NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA,
11330
- NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
11331
- });
11332
- /**
11333
- * Public URL of the deployment, e.g. "https://my-app.vercel.app"
11334
- *
11335
- * Note: When a request resolves through the global `_Server` registry,
11336
- * this URL will be overridden by the matched server domain.
11337
- */
11338
- config.get('NEXT_PUBLIC_SITE_URL').url().value;
11339
- /**
11340
- * [♐️] Vercel environment: "development" | "preview" | "production"
11341
- */
11342
- config.get('NEXT_PUBLIC_VERCEL_ENV').value;
11343
- /**
11344
- * [♐️] Target environment – can be system or custom
11345
- */
11346
- config.get('NEXT_PUBLIC_VERCEL_TARGET_ENV').value;
11347
- /**
11348
- * [♐️] Deployment URL (without https://), e.g. "my-app-abc123.vercel.app"
11349
- */
11350
- config.get('NEXT_PUBLIC_VERCEL_URL').value;
11351
- /**
11352
- * [♐️] Branch URL (without https://), only for branch deployments
11353
- */
11354
- config.get('NEXT_PUBLIC_VERCEL_BRANCH_URL').value;
11355
- /**
11356
- * [♐️] Production domain of the project
11357
- */
11358
- config.get('NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL').value;
11359
- /**
11360
- * [♐️] Git provider (github | gitlab | bitbucket)
11361
- */
11362
- config.get('NEXT_PUBLIC_VERCEL_GIT_PROVIDER').value;
11363
- /**
11364
- * [♐️] Repository owner (e.g. "hejny")
11365
- */
11366
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER').value;
11367
- /**
11368
- * [♐️] Repository slug (e.g. "my-project")
11369
- */
11370
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG').value;
11371
- /**
11372
- * [♐️] Repository internal ID
11373
- */
11374
- config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_ID').value;
11375
- /**
11376
- * [♐️] Git commit SHA (short or long)
11377
- */
11378
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA').value;
11379
- /**
11380
- * [♐️] Commit message used for this deployment
11381
- */
11382
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE').value;
11383
- /**
11384
- * [♐️] Branch name (ref), e.g. "main"
11385
- */
11386
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF').value;
11387
- /**
11388
- * Author name of the commit
11389
- */
11390
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME').value;
11391
- /**
11392
- * [♐️] Author login/username
11393
- */
11394
- config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN').value;
11395
- /**
11396
- * [♐️] Previous deployment commit SHA (if exists)
11397
- */
11398
- config.get('NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA').value;
11399
- /**
11400
- * [♐️] Pull Request ID for PR-based deployments
11401
- */
11402
- config.get('NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID').value;
11403
- /**
11404
- * Supabase table prefix
11405
- *
11406
- * This remains the fallback/default prefix used before `_Server` contains records
11407
- * or for local development requests.
11408
- */
11409
- config.get('SUPABASE_TABLE_PREFIX').value;
11410
- /**
11411
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
11412
- *
11413
- * When set, browser automation will connect to this remote server instead of launching a local browser.
11414
- * This is useful for environments like Vercel where running a full browser locally is not possible.
11415
- * Leave empty to use local browser mode.
11416
- */
11417
- const rawRemoteBrowserUrl = config.get('REMOTE_BROWSER_URL').value;
11418
- /**
11419
- * WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
11420
- *
11421
- * When set, browser automation will connect to this remote server instead of launching a local browser.
11422
- * This is useful for environments like Vercel where running a full browser locally is not possible.
11423
- * Leave empty to use local browser mode.
11424
- */
11425
- const REMOTE_BROWSER_URL = typeof rawRemoteBrowserUrl === 'string' ? rawRemoteBrowserUrl : '';
11426
-
11427
- /**
11428
- * Reads a positive integer value from environment variables.
11429
- */
11430
- function resolvePositiveIntFromEnv$1(variableName, defaultValue) {
11431
- const rawValue = process.env[variableName];
11432
- if (!rawValue || !rawValue.trim()) {
11433
- return defaultValue;
11434
- }
11435
- const parsed = Number.parseInt(rawValue.trim(), 10);
11436
- if (!Number.isFinite(parsed) || parsed <= 0) {
11437
- return defaultValue;
11438
- }
11439
- return parsed;
11440
- }
11441
- /**
11442
- * Runtime helpers for mode/session/timeout handling in `run_browser`.
11443
- *
11444
- * @private function of `run_browser`
11445
- */
11446
- const runBrowserRuntime = {
11447
- /**
11448
- * Creates a dedicated session id for one tool invocation.
11449
- */
11450
- createRunBrowserSessionId() {
11451
- return `${runBrowserConstants.sessionPrefix}-${randomUUID()}`;
11452
- },
11453
- /**
11454
- * Determines whether the browser tool is running in local or remote mode.
11455
- */
11456
- resolveExecutionMode() {
11457
- return REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0 ? 'remote' : 'local';
11458
- },
11459
- /**
11460
- * Converts the execution mode into a human-readable label.
11461
- */
11462
- formatExecutionMode(mode) {
11463
- return mode === 'remote' ? 'remote-browser' : 'local-browser';
11464
- },
11465
- /**
11466
- * Resolves timeout configuration from env defaults and optional call overrides.
11467
- */
11468
- resolveTimeoutConfiguration(overrides) {
11469
- const envNavigationTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_NAVIGATION_TIMEOUT_MS', runBrowserConstants.defaultNavigationTimeoutMs);
11470
- const envActionTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_ACTION_TIMEOUT_MS', runBrowserConstants.defaultActionTimeoutMs);
11471
- const navigationTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.navigationMs) && Number.isFinite(overrides.navigationMs) && overrides.navigationMs > 0
11472
- ? Math.floor(overrides.navigationMs)
11473
- : envNavigationTimeoutMs;
11474
- const actionTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.actionMs) && Number.isFinite(overrides.actionMs) && overrides.actionMs > 0
11475
- ? Math.floor(overrides.actionMs)
11476
- : envActionTimeoutMs;
11477
- return {
11478
- navigationTimeoutMs,
11479
- actionTimeoutMs,
11480
- };
11481
- },
11482
- };
11483
-
11484
- /**
11485
- * Error classification and cancellation helpers used by `run_browser`.
11486
- *
11487
- * @private function of `run_browser`
11488
- */
11489
- const runBrowserErrorHandling = {
11490
- /**
11491
- * Creates one tagged ParseError used for deterministic input validation failures.
11492
- */
11493
- createRunBrowserValidationError(options) {
11494
- const error = new ParseError(options.message);
11495
- error.name = 'RunBrowserValidationError';
11496
- error.runBrowserCode = runBrowserConstants.validationErrorCode;
11497
- error.isRetryable = false;
11498
- error.suggestedNextSteps = [
11499
- 'Fix the action payload to match the run_browser schema.',
11500
- 'Check selectors and required action fields before retrying.',
11501
- ];
11502
- error.debug = options.debug;
11503
- return error;
11504
- },
11505
- /**
11506
- * Creates one tagged KnowledgeScrapeError used for navigation failures.
11507
- */
11508
- createRunBrowserNavigationError(options) {
11509
- const error = new KnowledgeScrapeError(options.message);
11510
- error.name = 'RunBrowserNavigationError';
11511
- error.runBrowserCode = runBrowserConstants.navigationFailedErrorCode;
11512
- error.isRetryable = false;
11513
- error.suggestedNextSteps = [
11514
- 'Verify the URL is reachable and not blocked.',
11515
- 'Retry with a simpler action sequence.',
11516
- ];
11517
- error.debug = options.debug;
11518
- error.cause = options.cause;
11519
- return error;
11520
- },
11521
- /**
11522
- * Creates one tagged KnowledgeScrapeError used for action failures.
11523
- */
11524
- createRunBrowserActionError(options) {
11525
- const error = new KnowledgeScrapeError(options.message);
11526
- error.name = 'RunBrowserActionError';
11527
- error.runBrowserCode = runBrowserConstants.actionFailedErrorCode;
11528
- error.isRetryable = false;
11529
- error.suggestedNextSteps = [
11530
- 'Verify selectors and action values.',
11531
- 'Reduce the action sequence to isolate the failing step.',
11532
- ];
11533
- error.debug = options.debug;
11534
- error.cause = options.cause;
11535
- return error;
11536
- },
11537
- /**
11538
- * Creates one tagged KnowledgeScrapeError used for cancellation.
11539
- */
11540
- createRunBrowserCancelledError(options) {
11541
- const error = new KnowledgeScrapeError(options.message);
11542
- error.name = 'RunBrowserCancelledError';
11543
- error.runBrowserCode = runBrowserConstants.cancelledErrorCode;
11544
- error.isRetryable = true;
11545
- error.suggestedNextSteps = [
11546
- 'Retry while request context is still active.',
11547
- 'Increase timeout if operation is expected to run longer.',
11548
- ];
11549
- error.debug = options.debug;
11550
- error.cause = options.cause;
11551
- return error;
11552
- },
11553
- /**
11554
- * Checks whether an unknown error carries run_browser classification tags.
11555
- */
11556
- isTaggedRunBrowserError(error) {
11557
- if (!error || typeof error !== 'object') {
11558
- return false;
11559
- }
11560
- const candidate = error;
11561
- return (typeof candidate.runBrowserCode === 'string' &&
11562
- typeof candidate.isRetryable === 'boolean' &&
11563
- Array.isArray(candidate.suggestedNextSteps) &&
11564
- typeof candidate.debug === 'object' &&
11565
- candidate.debug !== null);
11566
- },
11567
- /**
11568
- * Converts unknown errors into structured tool error payloads.
11569
- */
11570
- classifyRunBrowserToolError(options) {
11571
- if (isRemoteBrowserUnavailableError(options.error)) {
11572
- return {
11573
- code: options.error.code,
11574
- message: options.error.message,
11575
- isRetryable: options.error.isRetryable,
11576
- suggestedNextSteps: options.error.suggestedNextSteps,
11577
- debug: {
11578
- ...options.error.debug,
11579
- sessionId: options.sessionId,
11580
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
11581
- },
11582
- };
11583
- }
11584
- if (this.isTaggedRunBrowserError(options.error)) {
11585
- return {
11586
- code: options.error.runBrowserCode,
11587
- message: options.error.message,
11588
- isRetryable: options.error.isRetryable,
11589
- suggestedNextSteps: options.error.suggestedNextSteps,
11590
- debug: {
11591
- ...options.error.debug,
11592
- sessionId: options.sessionId,
11593
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
11594
- },
11595
- };
11596
- }
11597
- const remoteBrowserEndpoint = REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0
11598
- ? sanitizeRemoteBrowserEndpoint(REMOTE_BROWSER_URL.trim())
11599
- : null;
11600
- const message = getErrorMessage(options.error);
11601
- return {
11602
- code: runBrowserConstants.unknownErrorCode,
11603
- message,
11604
- isRetryable: false,
11605
- suggestedNextSteps: ['Inspect debug details to identify the failing phase.', 'Retry with fewer actions.'],
11606
- debug: {
11607
- sessionId: options.sessionId,
11608
- mode: runBrowserRuntime.formatExecutionMode(options.mode),
11609
- remoteBrowserEndpoint,
11610
- message,
11611
- stack: getErrorStack(options.error),
11612
- },
11613
- };
11614
- },
11615
- /**
11616
- * Asserts that the run was not aborted.
11617
- */
11618
- assertNotAborted(signal, sessionId) {
11619
- if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
11620
- return;
11621
- }
11622
- throw this.createRunBrowserCancelledError({
11623
- message: 'run_browser execution was cancelled.',
11624
- debug: { sessionId },
11625
- });
11626
- },
11627
- /**
11628
- * Returns true when the tool error represents remote browser unavailability.
11629
- */
11630
- isRemoteBrowserUnavailableCode(code) {
11631
- return code === REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
11632
- },
11633
- };
11634
-
11635
- /**
11636
- * In-memory observability counters for browser tool execution.
11637
- */
11638
- const RUN_BROWSER_OBSERVABILITY = {
11639
- totalRuns: 0,
11640
- fallbackRuns: 0,
11641
- errorCodeCounts: {},
11642
- };
11643
- /**
11644
- * Observability counters and metric logging for `run_browser`.
11645
- *
11646
- * @private function of `run_browser`
11647
- */
11648
- const runBrowserObservability = {
11649
- /**
11650
- * Increments total-run counter and returns the updated value.
11651
- */
11652
- incrementTotalRuns() {
11653
- RUN_BROWSER_OBSERVABILITY.totalRuns++;
11654
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
11655
- },
11656
- /**
11657
- * Returns current total run count.
11658
- */
11659
- getTotalRuns() {
11660
- return RUN_BROWSER_OBSERVABILITY.totalRuns;
11661
- },
11662
- /**
11663
- * Increments fallback counter and returns updated metrics.
11664
- */
11665
- incrementFallbackRunsAndGetMetrics() {
11666
- RUN_BROWSER_OBSERVABILITY.fallbackRuns++;
11667
- return {
11668
- fallbackRuns: RUN_BROWSER_OBSERVABILITY.fallbackRuns,
11669
- fallbackRate: RUN_BROWSER_OBSERVABILITY.totalRuns === 0
11670
- ? 0
11671
- : RUN_BROWSER_OBSERVABILITY.fallbackRuns / RUN_BROWSER_OBSERVABILITY.totalRuns,
11672
- };
11673
- },
11674
- /**
11675
- * Increments one error-code counter and returns the updated value.
11676
- */
11677
- incrementRunBrowserErrorCodeCounter(code) {
11678
- const currentValue = RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] || 0;
11679
- const nextValue = currentValue + 1;
11680
- RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] = nextValue;
11681
- return nextValue;
11682
- },
11683
- /**
11684
- * Writes one structured metric line for browser-tool observability.
11685
- */
11686
- logRunBrowserMetric(options) {
11687
- console.info('[run_browser][metric]', {
11688
- tool: 'run_browser',
11689
- mode: options.mode,
11690
- sessionId: options.sessionId,
11691
- event: options.event,
11692
- ...(options.payload || {}),
11693
- });
11694
- },
11695
- };
11696
-
11697
- /**
11698
- * Computes one compact preview of a fallback scrape payload.
11699
- */
11700
- function createContentPreview(content) {
11701
- const normalized = content.replace(/\s+/g, ' ').trim();
11702
- if (normalized.length <= 280) {
11703
- return normalized;
11704
- }
11705
- return `${normalized.slice(0, 277)}...`;
11706
- }
11707
- /**
11708
- * Payload and markdown formatters for `run_browser` outcomes.
11709
- *
11710
- * @private function of `run_browser`
11711
- */
11712
- const runBrowserResultFormatting = {
11713
- /**
11714
- * Produces one structured payload consumed by chat UI browser replay renderers.
11715
- */
11716
- createResultPayload(options) {
11717
- return {
11718
- schema: runBrowserConstants.resultSchema,
11719
- sessionId: options.sessionId,
11720
- mode: options.mode,
11721
- modeUsed: options.modeUsed,
11722
- initialUrl: options.initialUrl,
11723
- finalUrl: options.finalUrl,
11724
- finalTitle: options.finalTitle,
11725
- executedActions: options.executedActions,
11726
- artifacts: options.artifacts,
11727
- warning: options.warning,
11728
- error: options.error,
11729
- fallback: options.modeUsed === 'fallback' && options.fallbackContent !== null
11730
- ? {
11731
- scraper: 'fetch_url_content',
11732
- contentPreview: createContentPreview(options.fallbackContent),
11733
- }
11734
- : null,
11735
- timing: options.timing,
11736
- };
11737
- },
11738
- /**
11739
- * Produces a model-friendly markdown summary from browser execution artifacts.
11740
- */
11741
- formatSuccessResult(options) {
11742
- const { payload, snapshotPath } = options;
11743
- return spaceTrim$1((block) => {
11744
- var _a, _b, _c;
11745
- return `
11746
- # Browser run completed
11747
-
11748
- **Session:** ${payload.sessionId}
11749
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
11750
- **Mode used:** ${payload.modeUsed}
11751
- **Initial URL:** ${payload.initialUrl}
11752
- **Executed actions:** ${payload.executedActions.length}
11753
-
11754
- ## Final page
11755
-
11756
- - URL: ${payload.finalUrl || 'Unknown'}
11757
- - Title: ${payload.finalTitle || 'Unknown'}
11758
-
11759
- ## Timings
11760
-
11761
- - Connect: ${(_a = payload.timing.connectDurationMs) !== null && _a !== void 0 ? _a : 'Unknown'} ms
11762
- - Initial navigation: ${(_b = payload.timing.initialNavigationDurationMs) !== null && _b !== void 0 ? _b : 'Unknown'} ms
11763
- - Time to first byte: ${(_c = payload.timing.timeToFirstByteMs) !== null && _c !== void 0 ? _c : 'Unknown'} ms
11764
- - Total: ${payload.timing.totalDurationMs} ms
11765
-
11766
- ${payload.artifacts.length === 0
11767
- ? ''
11768
- : `
11769
- ## Visual replay
11770
-
11771
- ${payload.artifacts
11772
- .map((artifact, index) => {
11773
- const actionPart = artifact.actionSummary ? ` (${artifact.actionSummary})` : '';
11774
- return `- ${index + 1}. ${artifact.label}${actionPart}: ${artifact.path}`;
11775
- })
11776
- .join('\n')}
11777
- `}
11778
-
11779
- ${!snapshotPath
11780
- ? ''
11781
- : `
11782
- ## Final snapshot
11783
-
11784
- ${snapshotPath}
11785
- `}
11786
-
11787
- ## Playback payload
11788
-
11789
- \`\`\`json
11790
- ${JSON.stringify(payload, null, 2)}
11791
- \`\`\`
11792
-
11793
- ${block(payload.executedActions.length === 0
11794
- ? ''
11795
- : `
11796
- ## Action log
11797
-
11798
- ${payload.executedActions
11799
- .map((action, index) => `- ${index + 1}. ${JSON.stringify(action)}`)
11800
- .join('\n')}
11801
- `)}
11802
-
11803
- Note: Browser page has been automatically closed to free up resources.
11804
- `;
11805
- });
11806
- },
11807
- /**
11808
- * Produces a model-friendly markdown payload when fallback scraping is used.
11809
- */
11810
- formatFallbackResult(options) {
11811
- const { payload, fallbackContent, requestedActions } = options;
11812
- return spaceTrim$1(`
11813
- # Browser run completed with fallback
11814
-
11815
- **Session:** ${payload.sessionId}
11816
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
11817
- **Mode used:** ${payload.modeUsed}
11818
- **Initial URL:** ${payload.initialUrl}
11819
- **Requested actions:** ${requestedActions}
11820
- **Executed actions:** ${payload.executedActions.length}
11821
- **Warning:** ${payload.warning || runBrowserConstants.fallbackDynamicContentWarning}
11822
-
11823
- ## Extracted content
11824
-
11825
- ${fallbackContent}
11826
-
11827
- ## Playback payload
11828
-
11829
- \`\`\`json
11830
- ${JSON.stringify(payload, null, 2)}
11831
- \`\`\`
11832
- `);
11833
- },
11834
- /**
11835
- * Produces a model-friendly markdown error payload from browser execution failures.
11836
- */
11837
- formatErrorResult(options) {
11838
- const { payload } = options;
11839
- const toolError = payload.error;
11840
- const suggestedNextSteps = (toolError === null || toolError === void 0 ? void 0 : toolError.suggestedNextSteps) || [];
11841
- return spaceTrim$1(`
11842
- # Browser run failed
11843
-
11844
- **Session:** ${payload.sessionId}
11845
- **Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
11846
- **Mode used:** ${payload.modeUsed}
11847
- **Initial URL:** ${payload.initialUrl}
11848
- **Error code:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.code) || runBrowserConstants.unknownErrorCode}
11849
- **Error:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.message) || 'Unknown browser tool error'}
11850
-
11851
- ${suggestedNextSteps.length === 0
11852
- ? ''
11853
- : `
11854
- ## Suggested next steps
11855
-
11856
- ${suggestedNextSteps.map((step) => `- ${step}`).join('\n')}
11857
- `}
11858
-
11859
- ## Playback payload
11860
-
11861
- \`\`\`json
11862
- ${JSON.stringify(payload, null, 2)}
11863
- \`\`\`
11864
-
11865
- The browser tool could not complete the requested actions.
11866
- `);
11867
- },
11868
- };
11869
-
11870
- /**
11871
- * @@@
11872
- *
11873
- * @private within the repository
11874
- */
11875
- function locateChrome() {
11876
- return locateApp({
11877
- appName: 'Chrome',
11878
- linuxWhich: 'google-chrome',
11879
- windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
11880
- macOsName: 'Google Chrome',
11881
- });
11882
- }
11883
-
11884
- /**
11885
- * Creates one standard abort error for cancelled retry loops.
11886
- *
11887
- * @private utility for Agents Server runtime retries
11888
- */
11889
- function createAbortError$1() {
11890
- const error = new Error('Operation was aborted.');
11891
- error.name = 'AbortError';
11892
- return error;
11893
- }
11894
- /**
11895
- * Throws when the supplied signal is already aborted.
11896
- *
11897
- * @private utility for Agents Server runtime retries
11898
- */
11899
- function assertNotAborted(signal) {
11900
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
11901
- throw createAbortError$1();
11902
- }
11903
- }
11904
- /**
11905
- * Waits for a duration while respecting cancellation.
11906
- *
11907
- * @private utility for Agents Server runtime retries
11908
- */
11909
- async function sleepWithAbort(delayMs, signal) {
11910
- if (delayMs <= 0) {
11911
- assertNotAborted(signal);
11912
- return;
11913
- }
11914
- await new Promise((resolve, reject) => {
11915
- const timeout = setTimeout(() => {
11916
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
11917
- resolve();
11918
- }, delayMs);
11919
- const onAbort = () => {
11920
- clearTimeout(timeout);
11921
- signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
11922
- reject(createAbortError$1());
11923
- };
11924
- signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
11925
- });
11926
- }
11927
- /**
11928
- * Resolves the retry wait duration for one failed attempt.
11929
- *
11930
- * @private utility for Agents Server runtime retries
11931
- */
11932
- function resolveBackoffDelayMs(options) {
11933
- const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
11934
- const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
11935
- const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
11936
- return Math.max(0, Math.round(boundedDelay + jitterDelay));
11937
- }
11938
- /**
11939
- * Retries one async operation with exponential backoff and jitter.
11940
- *
11941
- * @private utility for Agents Server runtime retries
11942
- */
11943
- async function retryWithBackoff(operation, options) {
11944
- var _a, _b, _c;
11945
- const startedAt = Date.now();
11946
- const totalAttempts = Math.max(1, options.retries + 1);
11947
- const random = (_a = options.random) !== null && _a !== void 0 ? _a : Math.random;
11948
- const sleep = (_b = options.sleep) !== null && _b !== void 0 ? _b : sleepWithAbort;
11949
- for (let attempt = 1; attempt <= totalAttempts; attempt++) {
11950
- assertNotAborted(options.signal);
11951
- try {
11952
- const value = await operation(attempt);
11953
- return {
11954
- value,
11955
- attempts: attempt,
11956
- durationMs: Date.now() - startedAt,
11957
- };
11958
- }
11959
- catch (error) {
11960
- const isLastAttempt = attempt >= totalAttempts;
11961
- const isRetryable = options.shouldRetry ? options.shouldRetry(error, attempt) : true;
11962
- if (isLastAttempt || !isRetryable) {
11963
- throw error;
11964
- }
11965
- const delayMs = resolveBackoffDelayMs({
11966
- attempt,
11967
- initialDelayMs: options.initialDelayMs,
11968
- maxDelayMs: options.maxDelayMs,
11969
- backoffFactor: options.backoffFactor,
11970
- jitterRatio: options.jitterRatio,
11971
- random,
11972
- });
11973
- (_c = options.onRetry) === null || _c === void 0 ? void 0 : _c.call(options, {
11974
- attempt,
11975
- retries: options.retries,
11976
- delayMs,
11977
- error,
11978
- });
11979
- await sleep(delayMs, options.signal);
11980
- }
11981
- }
11982
- throw new Error('Retry loop exited unexpectedly.');
11983
- }
11984
-
11985
- const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
11986
- /**
11987
- * Default remote browser connect timeout in milliseconds.
11988
- */
11989
- const DEFAULT_REMOTE_CONNECT_TIMEOUT_MS = 10000;
11990
- /**
11991
- * Default retry count for remote browser connection establishment.
11992
- */
11993
- const DEFAULT_REMOTE_CONNECT_RETRIES = 2;
11994
- /**
11995
- * Default initial retry delay for remote browser connection.
11996
- */
11997
- const DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS = 250;
11998
- /**
11999
- * Default maximum retry delay for remote browser connection.
12000
- */
12001
- const DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS = 1000;
12002
- /**
12003
- * Default exponential multiplier for remote browser retry delay.
12004
- */
12005
- const DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR = 4;
12006
- /**
12007
- * Default retry jitter ratio for remote browser connection.
12008
- */
12009
- const DEFAULT_REMOTE_CONNECT_JITTER_RATIO = 0.2;
12010
- /**
12011
- * In-memory metrics counters for remote browser connect attempts.
12012
- */
12013
- const REMOTE_BROWSER_CONNECT_METRICS = {
12014
- success: 0,
12015
- failure: 0,
12016
- };
12017
- /**
12018
- * Reads a positive integer from environment variables with a fallback default.
12019
- */
12020
- function resolvePositiveIntFromEnv(variableName, defaultValue) {
12021
- const rawValue = process.env[variableName];
12022
- if (!rawValue || !rawValue.trim()) {
12023
- return defaultValue;
12024
- }
12025
- const parsed = Number.parseInt(rawValue.trim(), 10);
12026
- if (!Number.isFinite(parsed) || parsed <= 0) {
12027
- return defaultValue;
12028
- }
12029
- return parsed;
12030
- }
12031
- /**
12032
- * Reads a positive number from environment variables with a fallback default.
12033
- */
12034
- function resolvePositiveNumberFromEnv(variableName, defaultValue) {
12035
- const rawValue = process.env[variableName];
12036
- if (!rawValue || !rawValue.trim()) {
12037
- return defaultValue;
12038
- }
12039
- const parsed = Number.parseFloat(rawValue.trim());
12040
- if (!Number.isFinite(parsed) || parsed <= 0) {
12041
- return defaultValue;
12042
- }
12043
- return parsed;
12044
- }
12045
- /**
12046
- * Reads a non-negative integer from environment variables with a fallback default.
12047
- */
12048
- function resolveNonNegativeIntFromEnv(variableName, defaultValue) {
12049
- const rawValue = process.env[variableName];
12050
- if (!rawValue || !rawValue.trim()) {
12051
- return defaultValue;
12052
- }
12053
- const parsed = Number.parseInt(rawValue.trim(), 10);
12054
- if (!Number.isFinite(parsed) || parsed < 0) {
12055
- return defaultValue;
12056
- }
12057
- return parsed;
12058
- }
12059
- /**
12060
- * Reads a non-negative number from environment variables with a fallback default.
12061
- */
12062
- function resolveNonNegativeNumberFromEnv(variableName, defaultValue) {
12063
- const rawValue = process.env[variableName];
12064
- if (!rawValue || !rawValue.trim()) {
12065
- return defaultValue;
12066
- }
12067
- const parsed = Number.parseFloat(rawValue.trim());
12068
- if (!Number.isFinite(parsed) || parsed < 0) {
12069
- return defaultValue;
12070
- }
12071
- return parsed;
12072
- }
12073
- /**
12074
- * Creates one standard abort error.
12075
- */
12076
- function createAbortError() {
12077
- const error = new Error('Browser connection request was aborted.');
12078
- error.name = 'AbortError';
12079
- return error;
12080
- }
12081
- /**
12082
- * Provides browser context instances with support for both local and remote browser connections.
12083
- *
12084
- * This provider manages browser lifecycle and supports:
12085
- * - Local mode: Launches a persistent Chromium context on the same machine
12086
- * - Remote mode: Connects to a remote Playwright browser via WebSocket
12087
- *
12088
- * The remote mode is useful for environments like Vercel where running a full browser
12089
- * is not possible due to resource constraints.
12090
- *
12091
- * @private internal utility for Agents Server browser tools
12092
- */
12093
- class BrowserConnectionProvider {
12094
- /**
12095
- * Creates a new BrowserConnectionProvider.
12096
- *
12097
- * @param options - Provider options
12098
- * @param options.isVerbose - Enable verbose logging
12099
- */
12100
- constructor(options = {}) {
12101
- var _a, _b, _c, _d, _e, _f, _g, _h;
12102
- this.browserContext = null;
12103
- this.connectionMode = null;
12104
- this.isVerbose = (_a = options.isVerbose) !== null && _a !== void 0 ? _a : false;
12105
- this.remoteConnectTimeoutMs =
12106
- (_b = options.remoteConnectTimeoutMs) !== null && _b !== void 0 ? _b : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_TIMEOUT_MS', DEFAULT_REMOTE_CONNECT_TIMEOUT_MS);
12107
- this.remoteConnectRetries =
12108
- (_c = options.remoteConnectRetries) !== null && _c !== void 0 ? _c : resolveNonNegativeIntFromEnv('RUN_BROWSER_CONNECT_RETRIES', DEFAULT_REMOTE_CONNECT_RETRIES);
12109
- this.remoteConnectBackoffInitialMs =
12110
- (_d = options.remoteConnectBackoffInitialMs) !== null && _d !== void 0 ? _d : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_INITIAL_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS);
12111
- this.remoteConnectBackoffMaxMs =
12112
- (_e = options.remoteConnectBackoffMaxMs) !== null && _e !== void 0 ? _e : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_MAX_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS);
12113
- this.remoteConnectBackoffFactor =
12114
- (_f = options.remoteConnectBackoffFactor) !== null && _f !== void 0 ? _f : resolvePositiveNumberFromEnv('RUN_BROWSER_CONNECT_BACKOFF_FACTOR', DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR);
12115
- this.remoteConnectJitterRatio =
12116
- (_g = options.remoteConnectJitterRatio) !== null && _g !== void 0 ? _g : resolveNonNegativeNumberFromEnv('RUN_BROWSER_CONNECT_JITTER_RATIO', DEFAULT_REMOTE_CONNECT_JITTER_RATIO);
12117
- this.random = (_h = options.random) !== null && _h !== void 0 ? _h : Math.random;
12118
- this.sleep = options.sleep;
12119
- }
12120
- /**
12121
- * Gets a browser context, creating a new one if needed.
12122
- *
12123
- * This method automatically determines whether to use local or remote browser
12124
- * based on the REMOTE_BROWSER_URL environment variable.
12125
- *
12126
- * @returns Browser context instance
12127
- */
12128
- async getBrowserContext(options = {}) {
12129
- var _a;
12130
- if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
12131
- throw createAbortError();
12132
- }
12133
- // Check if we have a cached connection that's still valid
12134
- if (this.browserContext !== null && this.isBrowserContextAlive(this.browserContext)) {
12135
- return this.browserContext;
12136
- }
12137
- // Determine connection mode from configuration
12138
- const mode = this.resolveConnectionMode();
12139
- this.connectionMode = mode;
12140
- if (this.isVerbose) {
12141
- console.info('[BrowserConnectionProvider] Creating new browser context', {
12142
- mode: mode.type,
12143
- wsEndpoint: mode.type === 'remote' ? mode.wsEndpoint : undefined,
12144
- });
12145
- }
12146
- // Create new browser context based on mode
12147
- if (mode.type === 'local') {
12148
- this.browserContext = await this.createLocalBrowserContext();
12149
- }
12150
- else {
12151
- this.browserContext = await this.createRemoteBrowserContext(mode.wsEndpoint, options);
12152
- }
12153
- return this.browserContext;
12154
- }
12155
- /**
12156
- * Closes all pages in the current browser context.
12157
- *
12158
- * This method is useful for cleanup between agent tasks without closing
12159
- * the entire browser instance.
12160
- */
12161
- async closeAllPages() {
12162
- if (!this.browserContext) {
12163
- return;
12164
- }
12165
- try {
12166
- const pages = this.browserContext.pages();
12167
- if (this.isVerbose) {
12168
- console.info('[BrowserConnectionProvider] Closing all pages', {
12169
- pageCount: pages.length,
12170
- });
12171
- }
12172
- await Promise.all(pages.map((page) => page.close().catch((error) => {
12173
- console.error('[BrowserConnectionProvider] Failed to close page', { error });
12174
- })));
12175
- }
12176
- catch (error) {
12177
- console.error('[BrowserConnectionProvider] Error closing pages', { error });
12178
- }
12179
- }
12180
- /**
12181
- * Closes the browser context and disconnects from the browser.
12182
- *
12183
- * This should be called when the browser is no longer needed to free up resources.
12184
- * For local mode, this closes the browser process. For remote mode, it disconnects
12185
- * from the remote browser but doesn't shut down the remote server.
12186
- */
12187
- async close() {
12188
- var _a;
12189
- if (!this.browserContext) {
12190
- return;
12191
- }
12192
- try {
12193
- if (this.isVerbose) {
12194
- console.info('[BrowserConnectionProvider] Closing browser context', {
12195
- mode: (_a = this.connectionMode) === null || _a === void 0 ? void 0 : _a.type,
12196
- });
12197
- }
12198
- await this.browserContext.close();
12199
- this.browserContext = null;
12200
- this.connectionMode = null;
12201
- }
12202
- catch (error) {
12203
- console.error('[BrowserConnectionProvider] Error closing browser context', { error });
12204
- // Reset state even if close fails
12205
- this.browserContext = null;
12206
- this.connectionMode = null;
12207
- }
12208
- }
12209
- /**
12210
- * Checks if a browser context is still alive and connected.
12211
- *
12212
- * @param context - Browser context to check
12213
- * @returns True if the context is connected and usable
12214
- */
12215
- isBrowserContextAlive(context) {
12216
- try {
12217
- const browser = context.browser();
12218
- return browser !== null && browser.isConnected();
12219
- }
12220
- catch (_a) {
12221
- return false;
12222
- }
12223
- }
12224
- /**
12225
- * Determines whether to use local or remote browser based on configuration.
12226
- *
12227
- * @returns Connection mode configuration
12228
- */
12229
- resolveConnectionMode() {
12230
- const remoteBrowserUrl = REMOTE_BROWSER_URL;
12231
- if (remoteBrowserUrl && remoteBrowserUrl.trim().length > 0) {
12232
- return {
12233
- type: 'remote',
12234
- wsEndpoint: remoteBrowserUrl.trim(),
12235
- };
12236
- }
12237
- return { type: 'local' };
12238
- }
12239
- /**
12240
- * Creates a local browser context using persistent Chromium.
12241
- *
12242
- * @returns Local browser context
12243
- */
12244
- async createLocalBrowserContext() {
12245
- if (this.isVerbose) {
12246
- console.info('[BrowserConnectionProvider] Launching local browser context');
12247
- }
12248
- const userDataDir = join(DEFAULT_BROWSER_USER_DATA_DIR, 'run-browser');
12249
- await mkdir(userDataDir, { recursive: true });
12250
- const launchOptions = {
12251
- headless: false,
12252
- args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
12253
- };
12254
- try {
12255
- const chromePath = await locateChrome();
12256
- launchOptions.executablePath = chromePath;
12257
- }
12258
- catch (error) {
12259
- if (this.isVerbose) {
12260
- console.warn('[BrowserConnectionProvider] Could not locate system Chrome; using Playwright bundled Chromium', {
12261
- error: error instanceof Error ? error.message : String(error),
12262
- });
12263
- }
12264
- }
12265
- return await chromium.launchPersistentContext(userDataDir, launchOptions);
12266
- }
12267
- /**
12268
- * Creates a remote browser context by connecting to a Playwright server.
12269
- *
12270
- * @param wsEndpoint - WebSocket endpoint of the remote Playwright server
12271
- * @returns Remote browser context
12272
- */
12273
- async createRemoteBrowserContext(wsEndpoint, options) {
12274
- const endpointDebug = sanitizeRemoteBrowserEndpoint(wsEndpoint);
12275
- const startedAt = Date.now();
12276
- if (this.isVerbose) {
12277
- console.info('[BrowserConnectionProvider] Connecting to remote browser', {
12278
- endpoint: endpointDebug,
12279
- connectTimeoutMs: this.remoteConnectTimeoutMs,
12280
- retries: this.remoteConnectRetries,
12281
- });
12282
- }
12283
- let attempts = 0;
12284
- try {
12285
- const connectResult = await retryWithBackoff(async (attempt) => {
12286
- attempts = attempt;
12287
- return await chromium.connect(wsEndpoint, {
12288
- timeout: this.remoteConnectTimeoutMs,
12289
- });
12290
- }, {
12291
- retries: this.remoteConnectRetries,
12292
- initialDelayMs: this.remoteConnectBackoffInitialMs,
12293
- maxDelayMs: this.remoteConnectBackoffMaxMs,
12294
- backoffFactor: this.remoteConnectBackoffFactor,
12295
- jitterRatio: this.remoteConnectJitterRatio,
12296
- signal: options.signal,
12297
- shouldRetry: (error) => isRemoteBrowserInfrastructureError(error),
12298
- onRetry: ({ attempt, delayMs, error }) => {
12299
- console.warn('[run_browser][retry]', {
12300
- tool: 'run_browser',
12301
- mode: 'remote-browser',
12302
- sessionId: options.sessionId || null,
12303
- event: 'remote_browser_connect_retry',
12304
- attempt,
12305
- delayMs,
12306
- endpoint: endpointDebug,
12307
- errorCode: extractNetworkErrorCode(error),
12308
- error: getErrorMessage(error),
12309
- });
12310
- },
12311
- random: this.random,
12312
- sleep: this.sleep,
12313
- });
12314
- const browser = connectResult.value;
12315
- // For remote connections, we need to create a new context
12316
- // Note: Remote browsers don't support persistent contexts
12317
- const context = await browser.newContext();
12318
- REMOTE_BROWSER_CONNECT_METRICS.success++;
12319
- console.info('[run_browser][metric]', {
12320
- tool: 'run_browser',
12321
- mode: 'remote-browser',
12322
- sessionId: options.sessionId || null,
12323
- event: 'remote_browser_connect_success',
12324
- attempts: connectResult.attempts,
12325
- connectDurationMs: connectResult.durationMs,
12326
- endpoint: endpointDebug,
12327
- counter: REMOTE_BROWSER_CONNECT_METRICS.success,
12328
- });
12329
- if (this.isVerbose) {
12330
- console.info('[BrowserConnectionProvider] Successfully connected to remote browser');
12331
- }
12332
- return context;
12333
- }
12334
- catch (error) {
12335
- REMOTE_BROWSER_CONNECT_METRICS.failure++;
12336
- const durationMs = Date.now() - startedAt;
12337
- const remoteInfraUnavailable = isRemoteBrowserInfrastructureError(error);
12338
- if (remoteInfraUnavailable) {
12339
- const remoteBrowserUnavailableError = new RemoteBrowserUnavailableError({
12340
- message: `Remote browser is unavailable. Could not establish a websocket connection.`,
12341
- debug: {
12342
- endpoint: endpointDebug,
12343
- attempts: Math.max(1, attempts),
12344
- connectTimeoutMs: this.remoteConnectTimeoutMs,
12345
- durationMs,
12346
- networkErrorCode: extractNetworkErrorCode(error),
12347
- originalMessage: getErrorMessage(error),
12348
- },
12349
- cause: error,
12350
- });
12351
- console.warn('[run_browser][metric]', {
12352
- tool: 'run_browser',
12353
- mode: 'remote-browser',
12354
- sessionId: options.sessionId || null,
12355
- event: 'remote_browser_connect_failure',
12356
- errorCode: remoteBrowserUnavailableError.code,
12357
- attempts: Math.max(1, attempts),
12358
- connectDurationMs: durationMs,
12359
- endpoint: endpointDebug,
12360
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
12361
- });
12362
- throw remoteBrowserUnavailableError;
12363
- }
12364
- console.error('[run_browser][metric]', {
12365
- tool: 'run_browser',
12366
- mode: 'remote-browser',
12367
- sessionId: options.sessionId || null,
12368
- event: 'remote_browser_connect_failure',
12369
- errorCode: 'REMOTE_BROWSER_CONNECT_ERROR',
12370
- attempts: Math.max(1, attempts),
12371
- connectDurationMs: durationMs,
12372
- endpoint: endpointDebug,
12373
- error: getErrorMessage(error),
12374
- counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
12375
- });
12376
- throw error;
12377
- }
12378
- }
12379
- }
12380
-
12381
- /**
12382
- * Singleton instance of the browser connection provider.
12383
- *
12384
- * @private internal cache for `$provideBrowserForServer`
12385
- */
12386
- let browserProvider = null;
12387
- /**
12388
- * Provides a browser context for server-side operations, with caching to reuse instances.
12389
- *
12390
- * This function supports both local and remote browser connections based on environment configuration.
12391
- * Use REMOTE_BROWSER_URL environment variable to configure a remote Playwright server.
12392
- *
12393
- * @param options - Optional runtime request options used for cancellation and logging context.
12394
- * @returns Browser context instance
12395
- */
12396
- async function $provideBrowserForServer(options = {}) {
12397
- if (browserProvider === null) {
12398
- browserProvider = new BrowserConnectionProvider({ isVerbose: false });
12399
- }
12400
- return await browserProvider.getBrowserContext(options);
12401
- }
12402
- /**
12403
- * TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
12404
- */
12405
-
12406
- /**
12407
- * Attempts to compute time-to-first-byte from Playwright response timing.
12408
- */
12409
- function resolveTimeToFirstByteMs(response) {
12410
- if (!response) {
12411
- return null;
12412
- }
12413
- try {
12414
- const timing = response.request().timing();
12415
- if (typeof (timing === null || timing === void 0 ? void 0 : timing.responseStart) === 'number' &&
12416
- typeof (timing === null || timing === void 0 ? void 0 : timing.startTime) === 'number' &&
12417
- timing.responseStart >= timing.startTime) {
12418
- return Math.round(timing.responseStart - timing.startTime);
12419
- }
12420
- }
12421
- catch (_a) {
12422
- return null;
12423
- }
12424
- return null;
12425
- }
12426
- /**
12427
- * Page open, action normalization and action execution helpers for `run_browser`.
12428
- *
12429
- * @private function of `run_browser`
12430
- */
12431
- const runBrowserWorkflow = {
12432
- /**
12433
- * Opens a new browser page and navigates to the requested URL.
12434
- */
12435
- async openPageWithUrl(options) {
12436
- runBrowserErrorHandling.assertNotAborted(options.signal, options.sessionId);
12437
- const connectStartedAt = Date.now();
12438
- const browserContext = await $provideBrowserForServer({
12439
- signal: options.signal,
12440
- sessionId: options.sessionId,
12441
- });
12442
- const connectDurationMs = Date.now() - connectStartedAt;
12443
- const page = await browserContext.newPage();
12444
- page.setDefaultNavigationTimeout(options.timeouts.navigationTimeoutMs);
12445
- page.setDefaultTimeout(options.timeouts.actionTimeoutMs);
12446
- const navigationStartedAt = Date.now();
12447
- try {
12448
- const navigationResponse = await page.goto(options.url, {
12449
- waitUntil: 'domcontentloaded',
12450
- timeout: options.timeouts.navigationTimeoutMs,
12451
- });
12452
- return {
12453
- page,
12454
- connectDurationMs,
12455
- initialNavigationDurationMs: Date.now() - navigationStartedAt,
12456
- timeToFirstByteMs: resolveTimeToFirstByteMs(navigationResponse),
12457
- };
12458
- }
12459
- catch (error) {
12460
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
12461
- message: `Failed to navigate to \`${options.url}\`.`,
12462
- debug: {
12463
- phase: 'initial-navigation',
12464
- url: options.url,
12465
- navigationTimeoutMs: options.timeouts.navigationTimeoutMs,
12466
- },
12467
- cause: error,
12468
- });
12469
- }
12470
- },
12471
- /**
12472
- * Validates and normalizes browser actions received from the model.
12473
- */
12474
- normalizeActions(actions) {
12475
- if (!actions || actions.length === 0) {
12476
- return [];
12477
- }
12478
- return actions.map((action, index) => this.normalizeAction(action, index));
12479
- },
12480
- /**
12481
- * Validates and normalizes a single action.
12482
- */
12483
- normalizeAction(action, index) {
12484
- var _a, _b, _c;
12485
- switch (action.type) {
12486
- case 'navigate': {
12487
- const url = String(action.value || '').trim();
12488
- if (!url) {
12489
- throw runBrowserErrorHandling.createRunBrowserValidationError({
12490
- message: spaceTrim$1(`Action ${index + 1}: \`navigate\` requires non-empty \`value\` URL.`),
12491
- debug: {
12492
- actionIndex: index + 1,
12493
- actionType: action.type,
12494
- },
12495
- });
12496
- }
12497
- return { type: 'navigate', url };
12498
- }
12499
- case 'click': {
12500
- const selector = String(action.selector || '').trim();
12501
- if (!selector) {
12502
- throw runBrowserErrorHandling.createRunBrowserValidationError({
12503
- message: spaceTrim$1(`Action ${index + 1}: \`click\` requires non-empty \`selector\`.`),
12504
- debug: {
12505
- actionIndex: index + 1,
12506
- actionType: action.type,
12507
- },
12508
- });
12509
- }
12510
- return { type: 'click', selector };
12511
- }
12512
- case 'type': {
12513
- const selector = String(action.selector || '').trim();
12514
- if (!selector) {
12515
- throw runBrowserErrorHandling.createRunBrowserValidationError({
12516
- message: spaceTrim$1(`Action ${index + 1}: \`type\` requires non-empty \`selector\`.`),
12517
- debug: {
12518
- actionIndex: index + 1,
12519
- actionType: action.type,
12520
- },
12521
- });
12522
- }
12523
- const text = String((_a = action.value) !== null && _a !== void 0 ? _a : '');
12524
- return { type: 'type', selector, text };
12525
- }
12526
- case 'wait': {
12527
- const requestedValue = Number.parseInt(String((_b = action.value) !== null && _b !== void 0 ? _b : runBrowserConstants.defaultWaitMs), 10);
12528
- const milliseconds = Number.isFinite(requestedValue)
12529
- ? Math.min(Math.max(requestedValue, 1), runBrowserConstants.maxWaitMs)
12530
- : runBrowserConstants.defaultWaitMs;
12531
- return { type: 'wait', milliseconds };
12532
- }
12533
- case 'scroll': {
12534
- const requestedValue = Number.parseInt(String((_c = action.value) !== null && _c !== void 0 ? _c : runBrowserConstants.defaultScrollPixels), 10);
12535
- const pixels = Number.isFinite(requestedValue) ? requestedValue : runBrowserConstants.defaultScrollPixels;
12536
- const rawSelector = String(action.selector || '').trim();
12537
- return { type: 'scroll', selector: rawSelector || null, pixels };
12538
- }
12539
- }
12540
- },
12541
- /**
12542
- * Executes one normalized browser action on a Playwright page.
12543
- */
12544
- async executeAction(options) {
12545
- const { page, action, actionIndex, timeouts, signal } = options;
12546
- runBrowserErrorHandling.assertNotAborted(signal, `action-${actionIndex}`);
12547
- try {
12548
- switch (action.type) {
12549
- case 'navigate':
12550
- await page.goto(action.url, {
12551
- waitUntil: 'domcontentloaded',
12552
- timeout: timeouts.navigationTimeoutMs,
12553
- });
12554
- return;
12555
- case 'click':
12556
- await page.locator(action.selector).first().click({ timeout: timeouts.actionTimeoutMs });
12557
- return;
12558
- case 'type':
12559
- await page.locator(action.selector).first().fill(action.text, { timeout: timeouts.actionTimeoutMs });
12560
- return;
12561
- case 'wait':
12562
- if (action.milliseconds > timeouts.actionTimeoutMs) {
12563
- throw runBrowserErrorHandling.createRunBrowserActionError({
12564
- message: `Action ${actionIndex}: \`wait\` exceeds action timeout (${timeouts.actionTimeoutMs}ms).`,
12565
- debug: {
12566
- actionIndex,
12567
- action,
12568
- actionTimeoutMs: timeouts.actionTimeoutMs,
12569
- },
12570
- });
12571
- }
12572
- await page.waitForTimeout(action.milliseconds);
12573
- return;
12574
- case 'scroll':
12575
- if (action.selector) {
12576
- await page
12577
- .locator(action.selector)
12578
- .first()
12579
- .scrollIntoViewIfNeeded({ timeout: timeouts.actionTimeoutMs });
12580
- }
12581
- await page.mouse.wheel(0, action.pixels);
12582
- return;
12583
- }
12584
- }
12585
- catch (error) {
12586
- if (runBrowserErrorHandling.isTaggedRunBrowserError(error)) {
12587
- throw error;
12588
- }
12589
- if (action.type === 'navigate') {
12590
- throw runBrowserErrorHandling.createRunBrowserNavigationError({
12591
- message: `Action ${actionIndex}: failed to navigate to \`${action.url}\`.`,
12592
- debug: {
12593
- actionIndex,
12594
- action,
12595
- navigationTimeoutMs: timeouts.navigationTimeoutMs,
12596
- },
12597
- cause: error,
12598
- });
12599
- }
12600
- throw runBrowserErrorHandling.createRunBrowserActionError({
12601
- message: `Action ${actionIndex}: failed to execute \`${action.type}\`.`,
12602
- debug: {
12603
- actionIndex,
12604
- action,
12605
- actionTimeoutMs: timeouts.actionTimeoutMs,
12606
- },
12607
- cause: error,
12608
- });
12609
- }
12610
- },
12611
- };
12612
-
12613
- /**
12614
- * Summarizes one normalized browser action in user-facing language.
12615
- */
12616
- function formatRunBrowserActionSummary(action) {
12617
- switch (action.type) {
12618
- case 'navigate':
12619
- return `Navigate to ${action.url}`;
12620
- case 'click':
12621
- return `Click ${action.selector}`;
12622
- case 'type':
12623
- return `Type into ${action.selector}`;
12624
- case 'wait':
12625
- return `Wait ${action.milliseconds}ms`;
12626
- case 'scroll':
12627
- return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
12628
- }
12629
- }
12630
- /**
12631
- * Emits one incremental browser-tool update when a hidden chat-progress listener is attached.
12632
- */
12633
- function emitRunBrowserProgress(args, update) {
12634
- emitToolCallProgressFromToolArgs(args, update);
12635
- }
12636
- /**
12637
- * Returns the current timestamp in the branded ISO-8601 format used by tool-call logs.
12638
- */
12639
- function createRunBrowserLogTimestamp() {
12640
- return new Date().toISOString();
12641
- }
12642
- /**
12643
- * Executes non-graphical fallback scraping.
12644
- */
12645
- async function runFallbackScrape(url) {
12646
- return await fetchUrlContent(url);
12647
- }
12648
- /**
12649
- * Runs interactive browser automation through Playwright.
12650
- *
12651
- * @param args Tool arguments provided by the model.
12652
- * @param internalOptions Optional runtime options for cancellation.
12653
- * @returns Markdown summary with structured playback payload.
12654
- */
12655
- async function run_browser(args, internalOptions = {}) {
12656
- runBrowserObservability.incrementTotalRuns();
12657
- const startedAt = Date.now();
12658
- const sessionId = runBrowserRuntime.createRunBrowserSessionId();
12659
- const initialUrl = String(args.url || '').trim();
12660
- const mode = runBrowserRuntime.resolveExecutionMode();
12661
- const timeoutConfiguration = runBrowserRuntime.resolveTimeoutConfiguration(args.timeouts);
12662
- let page = null;
12663
- let connectDurationMs = null;
12664
- let initialNavigationDurationMs = null;
12665
- let timeToFirstByteMs = null;
12666
- try {
12667
- if (!initialUrl) {
12668
- throw runBrowserErrorHandling.createRunBrowserValidationError({
12669
- message: 'Missing required `url` argument.',
12670
- debug: {
12671
- field: 'url',
12672
- },
12673
- });
12674
- }
12675
- const normalizedActions = runBrowserWorkflow.normalizeActions(args.actions);
12676
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
12677
- const openedPage = await runBrowserWorkflow.openPageWithUrl({
12678
- url: initialUrl,
12679
- sessionId,
12680
- timeouts: timeoutConfiguration,
12681
- signal: internalOptions.signal,
12682
- });
12683
- page = openedPage.page;
12684
- connectDurationMs = openedPage.connectDurationMs;
12685
- initialNavigationDurationMs = openedPage.initialNavigationDurationMs;
12686
- timeToFirstByteMs = openedPage.timeToFirstByteMs;
12687
- emitRunBrowserProgress(args, {
12688
- state: 'PARTIAL',
12689
- log: {
12690
- createdAt: createRunBrowserLogTimestamp(),
12691
- kind: 'browser-session',
12692
- title: 'Browser ready',
12693
- message: 'Opened the initial page and started the browser session.',
12694
- payload: {
12695
- sessionId,
12696
- initialUrl,
12697
- connectDurationMs,
12698
- initialNavigationDurationMs,
12699
- timeToFirstByteMs,
12700
- },
12701
- },
12702
- });
12703
- const artifacts = [];
12704
- const initialArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
12705
- page,
12706
- sessionId,
12707
- label: 'Initial page',
12708
- fileSuffix: 'initial',
12709
- });
12710
- if (initialArtifact) {
12711
- artifacts.push(initialArtifact);
12712
- }
12713
- for (const [index, action] of normalizedActions.entries()) {
12714
- runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
12715
- emitRunBrowserProgress(args, {
12716
- state: 'PARTIAL',
12717
- log: {
12718
- createdAt: createRunBrowserLogTimestamp(),
12719
- kind: 'browser-action',
12720
- title: `Action ${index + 1} running`,
12721
- message: formatRunBrowserActionSummary(action),
12722
- payload: {
12723
- actionIndex: index + 1,
12724
- action,
12725
- phase: 'running',
12726
- },
12727
- },
12728
- });
12729
- await runBrowserWorkflow.executeAction({
12730
- page,
12731
- action,
12732
- actionIndex: index + 1,
12733
- timeouts: timeoutConfiguration,
12734
- signal: internalOptions.signal,
12735
- });
12736
- emitRunBrowserProgress(args, {
12737
- state: 'PARTIAL',
12738
- log: {
12739
- createdAt: createRunBrowserLogTimestamp(),
12740
- kind: 'browser-action',
12741
- title: `Action ${index + 1} finished`,
12742
- message: formatRunBrowserActionSummary(action),
12743
- payload: {
12744
- actionIndex: index + 1,
12745
- action,
12746
- phase: 'complete',
12747
- },
12748
- },
12749
- });
12750
- const actionArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
12751
- page,
12752
- sessionId,
12753
- label: `After action ${index + 1}`,
12754
- fileSuffix: `action-${String(index + 1).padStart(3, '0')}-${action.type}`,
12755
- actionIndex: index + 1,
12756
- action,
12757
- });
12758
- if (actionArtifact) {
12759
- artifacts.push(actionArtifact);
12760
- }
12761
- }
12762
- const snapshotPath = await runBrowserArtifacts.captureSnapshot(page, sessionId);
12763
- const finalUrl = page.url();
12764
- const finalTitle = await runBrowserArtifacts.getPageTitle(page);
12765
- if (snapshotPath) {
12766
- artifacts.push({
12767
- kind: 'screenshot',
12768
- label: 'Final page',
12769
- path: snapshotPath,
12770
- capturedAt: new Date().toISOString(),
12771
- url: finalUrl,
12772
- title: finalTitle,
12773
- });
12774
- }
12775
- const payload = runBrowserResultFormatting.createResultPayload({
12776
- sessionId,
12777
- mode,
12778
- modeUsed: 'remote-browser',
12779
- initialUrl,
12780
- finalUrl,
12781
- finalTitle,
12782
- executedActions: normalizedActions,
12783
- artifacts,
12784
- warning: null,
12785
- error: null,
12786
- fallbackContent: null,
12787
- timing: {
12788
- connectDurationMs,
12789
- initialNavigationDurationMs,
12790
- timeToFirstByteMs,
12791
- totalDurationMs: Date.now() - startedAt,
12792
- },
12793
- });
12794
- runBrowserObservability.logRunBrowserMetric({
12795
- event: 'run_browser_success',
12796
- sessionId,
12797
- mode: 'remote-browser',
12798
- payload: {
12799
- actions: normalizedActions.length,
12800
- connectDurationMs,
12801
- initialNavigationDurationMs,
12802
- timeToFirstByteMs,
12803
- },
12804
- });
12805
- return runBrowserResultFormatting.formatSuccessResult({
12806
- payload,
12807
- snapshotPath,
12808
- });
12809
- }
12810
- catch (error) {
12811
- const toolError = runBrowserErrorHandling.classifyRunBrowserToolError({
12812
- error,
12813
- sessionId,
12814
- mode,
12815
- });
12816
- const errorCodeCount = runBrowserObservability.incrementRunBrowserErrorCodeCounter(toolError.code);
12817
- if (runBrowserErrorHandling.isRemoteBrowserUnavailableCode(toolError.code) && initialUrl) {
12818
- const fallbackContent = await runFallbackScrape(initialUrl);
12819
- const { fallbackRuns, fallbackRate } = runBrowserObservability.incrementFallbackRunsAndGetMetrics();
12820
- emitRunBrowserProgress(args, {
12821
- state: 'PARTIAL',
12822
- log: {
12823
- createdAt: createRunBrowserLogTimestamp(),
12824
- kind: 'warning',
12825
- level: 'warning',
12826
- title: 'Fallback enabled',
12827
- message: 'Remote browser was unavailable, so fallback scraping was used instead.',
12828
- payload: {
12829
- errorCode: toolError.code,
12830
- initialUrl,
12831
- },
12832
- },
12833
- });
12834
- const payload = runBrowserResultFormatting.createResultPayload({
12835
- sessionId,
12836
- mode,
12837
- modeUsed: 'fallback',
12838
- initialUrl,
12839
- finalUrl: null,
12840
- finalTitle: null,
12841
- executedActions: [],
12842
- artifacts: [],
12843
- warning: runBrowserConstants.fallbackDynamicContentWarning,
12844
- error: toolError,
12845
- fallbackContent,
12846
- timing: {
12847
- connectDurationMs,
12848
- initialNavigationDurationMs,
12849
- timeToFirstByteMs,
12850
- totalDurationMs: Date.now() - startedAt,
12851
- },
12852
- });
12853
- runBrowserObservability.logRunBrowserMetric({
12854
- event: 'run_browser_fallback_used',
12855
- sessionId,
12856
- mode: 'fallback',
12857
- payload: {
12858
- errorCode: toolError.code,
12859
- errorCodeCount,
12860
- fallbackRuns,
12861
- totalRuns: runBrowserObservability.getTotalRuns(),
12862
- fallbackRate,
12863
- },
12864
- });
12865
- return runBrowserResultFormatting.formatFallbackResult({
12866
- payload,
12867
- fallbackContent,
12868
- requestedActions: Array.isArray(args.actions) ? args.actions.length : 0,
12869
- });
12870
- }
12871
- emitRunBrowserProgress(args, {
12872
- state: 'ERROR',
12873
- log: {
12874
- createdAt: createRunBrowserLogTimestamp(),
12875
- kind: 'error',
12876
- level: 'error',
12877
- title: 'Browser run failed',
12878
- message: toolError.message,
12879
- payload: {
12880
- code: toolError.code,
12881
- debug: toolError.debug,
12882
- },
12883
- },
12884
- });
12885
- const payload = runBrowserResultFormatting.createResultPayload({
12886
- sessionId,
12887
- mode,
12888
- modeUsed: 'remote-browser',
12889
- initialUrl,
12890
- finalUrl: page ? page.url() : null,
12891
- finalTitle: page ? await runBrowserArtifacts.getPageTitle(page) : null,
12892
- executedActions: [],
12893
- artifacts: [],
12894
- warning: null,
12895
- error: toolError,
12896
- fallbackContent: null,
12897
- timing: {
12898
- connectDurationMs,
12899
- initialNavigationDurationMs,
12900
- timeToFirstByteMs,
12901
- totalDurationMs: Date.now() - startedAt,
12902
- },
12903
- });
12904
- runBrowserObservability.logRunBrowserMetric({
12905
- event: 'run_browser_failed',
12906
- sessionId,
12907
- mode: 'remote-browser',
12908
- payload: {
12909
- errorCode: toolError.code,
12910
- errorCodeCount,
12911
- connectDurationMs,
12912
- initialNavigationDurationMs,
12913
- timeToFirstByteMs,
12914
- },
12915
- });
12916
- return runBrowserResultFormatting.formatErrorResult({
12917
- payload,
12918
- });
12919
- }
12920
- finally {
12921
- await runBrowserArtifacts.cleanupPage(page, sessionId);
12922
- }
12923
- }
12924
-
12925
- /**
12926
- * Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
12927
- *
12928
- * This uses lazy `require` to keep the core package decoupled from Agents Server internals.
12929
- * When the server tool cannot be resolved, the fallback implementation throws a helpful error.
12930
- *
12931
- * @private internal utility for USE BROWSER commitment
12932
- */
12933
- function resolveRunBrowserToolForNode() {
12934
- try {
12935
- // eslint-disable-next-line @typescript-eslint/no-var-requires
12936
- // const { run_browser } = require('../../../apps/agents-server/src/tools/run_browser');
12937
- if (typeof run_browser !== 'function') {
12938
- throw new Error('run_browser value is not a function but ' + typeof run_browser);
12939
- }
12940
- return run_browser;
12941
- }
12942
- catch (error) {
12943
- assertsError(error);
12944
- return async () => {
12945
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
12946
- \`run_browser\` tool is not available in this environment.
12947
- This commitment requires the Agents Server browser runtime with Playwright CLI.
12948
-
12949
- ${error.name}:
12950
- ${block(error.message)}
12951
- `));
12952
- };
12953
- }
12954
- }
12955
-
12956
- /**
12957
- * Resolves the server-side implementation of the send_email tool for Node.js environments.
12958
- *
12959
- * This uses a lazy require so the core package can still load even if the Agents Server
12960
- * module is unavailable. When the server tool cannot be resolved, a fallback implementation
12961
- * throws a helpful error message.
12962
- *
12963
- * @private internal utility for USE EMAIL commitment
12964
- */
12965
- function resolveSendEmailToolForNode() {
12966
- try {
12967
- // eslint-disable-next-line @typescript-eslint/no-var-requires
12968
- const { send_email } = require('../../../apps/agents-server/src/tools/send_email');
12969
- if (typeof send_email !== 'function') {
12970
- throw new Error('send_email value is not a function but ' + typeof send_email);
12971
- }
12972
- return send_email;
12973
- }
12974
- catch (error) {
12975
- const normalizedError = error instanceof Error
12976
- ? error
12977
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
12978
- return async () => {
12979
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
12980
- \`send_email\` tool is not available in this environment.
12981
- This commitment requires Agents Server runtime with wallet-backed SMTP sending.
12982
-
12983
- ${normalizedError.name}:
12984
- ${block(normalizedError.message)}
12985
- `));
12986
- };
12987
- }
12988
- }
12989
-
12990
- /**
12991
- * Resolves the server-side `spawn_agent` tool for Node.js runtimes.
12992
- *
12993
- * Uses lazy require so core package can load outside Agents Server.
12994
- *
12995
- * @private internal utility for USE SPAWN commitment
12996
- */
12997
- function resolveSpawnAgentToolForNode() {
12998
- try {
12999
- // eslint-disable-next-line @typescript-eslint/no-var-requires
13000
- const { spawn_agent } = require('../../../apps/agents-server/src/tools/spawn_agent');
13001
- if (typeof spawn_agent !== 'function') {
13002
- throw new Error('spawn_agent value is not a function but ' + typeof spawn_agent);
13003
- }
13004
- return spawn_agent;
13005
- }
13006
- catch (error) {
13007
- const normalizedError = error instanceof Error
13008
- ? error
13009
- : new Error(typeof error === 'string' ? error : JSON.stringify(error !== null && error !== void 0 ? error : 'Unknown error'));
13010
- return async () => {
13011
- throw new EnvironmentMismatchError(spaceTrim$1((block) => `
13012
- \`spawn_agent\` tool is not available in this environment.
13013
- This commitment requires Agents Server runtime with agent persistence enabled.
13014
-
13015
- ${normalizedError.name}:
13016
- ${block(normalizedError.message)}
13017
- `));
13018
- };
13019
- }
13020
- }
13021
-
13022
- /**
13023
- * Generates a regex pattern to match a specific commitment
13024
- *
13025
- * Note: It always creates new Regex object
13026
- * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
13027
- *
13028
- * @private - TODO: [🧠] Maybe should be public?
13029
- */
13030
- function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
13031
- const allCommitments = [commitment, ...aliases];
13032
- const patterns = allCommitments.map((commitment) => {
13033
- const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13034
- return escapedCommitment.split(/\s+/).join('\\s+');
13035
- });
13036
- const keywordPattern = patterns.join('|');
13037
- if (requiresContent) {
13038
- return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
13039
- }
13040
- else {
13041
- return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
13042
- }
13043
- }
13044
- /**
13045
- * Generates a regex pattern to match a specific commitment type
13046
- *
13047
- * Note: It just matches the type part of the commitment
13048
- * Note: It always creates new Regex object
13049
- * Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
13050
- *
13051
- * @private
13052
- */
13053
- function createCommitmentTypeRegex(commitment, aliases = []) {
13054
- const allCommitments = [commitment, ...aliases];
13055
- const patterns = allCommitments.map((commitment) => {
13056
- const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13057
- return escapedCommitment.split(/\s+/).join('\\s+');
13058
- });
13059
- const keywordPattern = patterns.join('|');
13060
- const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
13061
- return regex;
13062
- }
13063
-
13064
- /**
13065
- * Base implementation of CommitmentDefinition that provides common functionality
13066
- * Most commitments can extend this class and only override the applyToAgentModelRequirements method
13067
- *
13068
- * @private
13069
- */
13070
- class BaseCommitmentDefinition {
13071
- constructor(type, aliases = []) {
13072
- this.type = type;
13073
- this.aliases = aliases;
11057
+ class BaseCommitmentDefinition {
11058
+ constructor(type, aliases = []) {
11059
+ this.type = type;
11060
+ this.aliases = aliases;
13074
11061
  }
13075
11062
  /**
13076
11063
  * Whether this commitment requires content.
@@ -14716,74 +12703,178 @@ function getMemoryToolRuntimeAdapterOrDisabledResult(action, runtimeContext) {
14716
12703
  };
14717
12704
  }
14718
12705
  }
14719
-
12706
+
12707
+ /**
12708
+ * Parses a memory identifier argument shared across MEMORY tools.
12709
+ *
12710
+ * @private function of MemoryCommitmentDefinition
12711
+ */
12712
+ function parseMemoryIdArg(value) {
12713
+ const memoryId = typeof value === 'string' ? value.trim() : '';
12714
+ if (!memoryId) {
12715
+ throw new Error('Memory id is required.');
12716
+ }
12717
+ return memoryId;
12718
+ }
12719
+ /**
12720
+ * Collection of MEMORY tool argument parsers.
12721
+ *
12722
+ * @private function of MemoryCommitmentDefinition
12723
+ */
12724
+ const parseMemoryToolArgs = {
12725
+ /**
12726
+ * Parses retrieve memory arguments.
12727
+ */
12728
+ retrieve(args) {
12729
+ const query = typeof args.query === 'string' ? args.query.trim() : undefined;
12730
+ const limit = typeof args.limit === 'number' && Number.isFinite(args.limit) ? Math.floor(args.limit) : undefined;
12731
+ return {
12732
+ query: query && query.length > 0 ? query : undefined,
12733
+ limit: limit && limit > 0 ? Math.min(limit, 20) : undefined,
12734
+ };
12735
+ },
12736
+ /**
12737
+ * Parses store memory arguments.
12738
+ */
12739
+ store(args) {
12740
+ const content = typeof args.content === 'string' ? args.content.trim() : '';
12741
+ if (!content) {
12742
+ throw new Error('Memory content is required.');
12743
+ }
12744
+ return {
12745
+ content,
12746
+ isGlobal: args.isGlobal === true,
12747
+ };
12748
+ },
12749
+ /**
12750
+ * Parses update memory arguments.
12751
+ */
12752
+ update(args) {
12753
+ const memoryId = parseMemoryIdArg(args.memoryId);
12754
+ const content = typeof args.content === 'string' ? args.content.trim() : '';
12755
+ if (!content) {
12756
+ throw new Error('Memory content is required.');
12757
+ }
12758
+ const isGlobal = typeof args.isGlobal === 'boolean' ? args.isGlobal : undefined;
12759
+ return {
12760
+ memoryId,
12761
+ content,
12762
+ isGlobal,
12763
+ };
12764
+ },
12765
+ /**
12766
+ * Parses delete memory arguments.
12767
+ */
12768
+ delete(args) {
12769
+ return {
12770
+ memoryId: parseMemoryIdArg(args.memoryId),
12771
+ };
12772
+ },
12773
+ };
12774
+
12775
+ /**
12776
+ * Prompt parameter key used to pass hidden runtime context to tool execution.
12777
+ *
12778
+ * @private internal runtime wiring for commitment tools
12779
+ */
12780
+ const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
12781
+ /**
12782
+ * Hidden argument key used to pass runtime context into individual tool calls.
12783
+ *
12784
+ * @private internal runtime wiring for commitment tools
12785
+ */
12786
+ const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
12787
+ /**
12788
+ * Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
12789
+ *
12790
+ * @private internal runtime wiring for commitment tools
12791
+ */
12792
+ const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
12793
+ /**
12794
+ * Hidden argument key used to pass a tool-progress listener token into individual tool calls.
12795
+ *
12796
+ * @private internal runtime wiring for commitment tools
12797
+ */
12798
+ const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
12799
+ /**
12800
+ * Monotonic counter used for hidden progress-listener tokens.
12801
+ *
12802
+ * @private internal runtime wiring for commitment tools
12803
+ */
12804
+ let toolCallProgressListenerCounter = 0;
12805
+ /**
12806
+ * Active tool-progress listeners keyed by hidden execution token.
12807
+ *
12808
+ * @private internal runtime wiring for commitment tools
12809
+ */
12810
+ const toolCallProgressListeners = new Map();
12811
+ /**
12812
+ * Parses unknown runtime context payload into a normalized object.
12813
+ *
12814
+ * @private internal runtime wiring for commitment tools
12815
+ */
12816
+ function parseToolRuntimeContext(rawValue) {
12817
+ if (!rawValue) {
12818
+ return null;
12819
+ }
12820
+ let parsed = rawValue;
12821
+ if (typeof rawValue === 'string') {
12822
+ try {
12823
+ parsed = JSON.parse(rawValue);
12824
+ }
12825
+ catch (_a) {
12826
+ return null;
12827
+ }
12828
+ }
12829
+ if (!parsed || typeof parsed !== 'object') {
12830
+ return null;
12831
+ }
12832
+ return parsed;
12833
+ }
12834
+ /**
12835
+ * Reads runtime context attached to tool call arguments.
12836
+ *
12837
+ * @private internal runtime wiring for commitment tools
12838
+ */
12839
+ function readToolRuntimeContextFromToolArgs(args) {
12840
+ return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
12841
+ }
12842
+ /**
12843
+ * Serializes runtime context for prompt parameters.
12844
+ *
12845
+ * @private internal runtime wiring for commitment tools
12846
+ */
12847
+ function serializeToolRuntimeContext(context) {
12848
+ return JSON.stringify(context);
12849
+ }
12850
+ /**
12851
+ * Registers one in-memory listener that receives progress updates emitted by a running tool.
12852
+ *
12853
+ * The returned token is passed into script execution as a hidden argument so tool implementations
12854
+ * can stream progress without exposing extra parameters to the model.
12855
+ *
12856
+ * @param listener - Listener notified about tool progress.
12857
+ * @returns Hidden token used to route progress updates.
12858
+ * @private internal runtime wiring for commitment tools
12859
+ */
12860
+ function registerToolCallProgressListener(listener) {
12861
+ toolCallProgressListenerCounter += 1;
12862
+ const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
12863
+ toolCallProgressListeners.set(token, listener);
12864
+ return token;
12865
+ }
14720
12866
  /**
14721
- * Parses a memory identifier argument shared across MEMORY tools.
12867
+ * Unregisters one in-memory progress listener.
14722
12868
  *
14723
- * @private function of MemoryCommitmentDefinition
12869
+ * @param token - Token previously created by `registerToolCallProgressListener`.
12870
+ * @private internal runtime wiring for commitment tools
14724
12871
  */
14725
- function parseMemoryIdArg(value) {
14726
- const memoryId = typeof value === 'string' ? value.trim() : '';
14727
- if (!memoryId) {
14728
- throw new Error('Memory id is required.');
14729
- }
14730
- return memoryId;
12872
+ function unregisterToolCallProgressListener(token) {
12873
+ toolCallProgressListeners.delete(token);
14731
12874
  }
14732
12875
  /**
14733
- * Collection of MEMORY tool argument parsers.
14734
- *
14735
- * @private function of MemoryCommitmentDefinition
12876
+ * Note: [💞] Ignore a discrepancy between file name and entity name
14736
12877
  */
14737
- const parseMemoryToolArgs = {
14738
- /**
14739
- * Parses retrieve memory arguments.
14740
- */
14741
- retrieve(args) {
14742
- const query = typeof args.query === 'string' ? args.query.trim() : undefined;
14743
- const limit = typeof args.limit === 'number' && Number.isFinite(args.limit) ? Math.floor(args.limit) : undefined;
14744
- return {
14745
- query: query && query.length > 0 ? query : undefined,
14746
- limit: limit && limit > 0 ? Math.min(limit, 20) : undefined,
14747
- };
14748
- },
14749
- /**
14750
- * Parses store memory arguments.
14751
- */
14752
- store(args) {
14753
- const content = typeof args.content === 'string' ? args.content.trim() : '';
14754
- if (!content) {
14755
- throw new Error('Memory content is required.');
14756
- }
14757
- return {
14758
- content,
14759
- isGlobal: args.isGlobal === true,
14760
- };
14761
- },
14762
- /**
14763
- * Parses update memory arguments.
14764
- */
14765
- update(args) {
14766
- const memoryId = parseMemoryIdArg(args.memoryId);
14767
- const content = typeof args.content === 'string' ? args.content.trim() : '';
14768
- if (!content) {
14769
- throw new Error('Memory content is required.');
14770
- }
14771
- const isGlobal = typeof args.isGlobal === 'boolean' ? args.isGlobal : undefined;
14772
- return {
14773
- memoryId,
14774
- content,
14775
- isGlobal,
14776
- };
14777
- },
14778
- /**
14779
- * Parses delete memory arguments.
14780
- */
14781
- delete(args) {
14782
- return {
14783
- memoryId: parseMemoryIdArg(args.memoryId),
14784
- };
14785
- },
14786
- };
14787
12878
 
14788
12879
  /**
14789
12880
  * Resolves runtime context from hidden tool arguments.
@@ -22493,9 +20584,9 @@ function createTimeoutSystemMessage(extraInstructions) {
22493
20584
  return spaceTrim$1((block) => `
22494
20585
  Timeout scheduling:
22495
20586
  - Use "set_timeout" to wake this same chat thread in the future.
22496
- - Timers are thread-scoped, not global for the whole agent.
20587
+ - Use "list_timeouts" to review timeouts across all chats for the same user+agent scope.
20588
+ - "cancel_timeout" accepts a timeout id from any chat in this same user+agent scope.
22497
20589
  - 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\`.
22498
- - Use "cancel_timeout" when a previously scheduled timeout is no longer relevant.
22499
20590
  - Do not claim a timer was set or cancelled unless the tool confirms it.
22500
20591
  ${block(extraInstructions)}
22501
20592
  `);
@@ -22556,13 +20647,6 @@ function parseToolExecutionEnvelope(rawValue) {
22556
20647
  * @private internal utility of USE TIMEOUT
22557
20648
  */
22558
20649
  function createDisabledTimeoutResult(action, message) {
22559
- if (action === 'set') {
22560
- return {
22561
- action,
22562
- status: 'disabled',
22563
- message,
22564
- };
22565
- }
22566
20650
  return {
22567
20651
  action,
22568
20652
  status: 'disabled',
@@ -22589,6 +20673,18 @@ function getTimeoutToolRuntimeAdapterOrDisabledResult(action, runtimeContext) {
22589
20673
  }
22590
20674
  }
22591
20675
 
20676
+ /**
20677
+ * Default number of rows returned by `list_timeouts`.
20678
+ *
20679
+ * @private internal USE TIMEOUT constant
20680
+ */
20681
+ const DEFAULT_LIST_TIMEOUTS_LIMIT = 20;
20682
+ /**
20683
+ * Hard cap for `list_timeouts` page size.
20684
+ *
20685
+ * @private internal USE TIMEOUT constant
20686
+ */
20687
+ const MAX_LIST_TIMEOUTS_LIMIT = 100;
22592
20688
  /**
22593
20689
  * Parses and validates `USE TIMEOUT` tool arguments.
22594
20690
  *
@@ -22623,6 +20719,31 @@ const parseTimeoutToolArgs = {
22623
20719
  }
22624
20720
  return { timeoutId };
22625
20721
  },
20722
+ /**
20723
+ * Parses `list_timeouts` input.
20724
+ */
20725
+ list(args) {
20726
+ if (args.includeFinished !== undefined && typeof args.includeFinished !== 'boolean') {
20727
+ throw new PipelineExecutionError(spaceTrim$1(`
20728
+ Timeout \`includeFinished\` must be a boolean when provided.
20729
+ `));
20730
+ }
20731
+ const parsedLimit = args.limit === undefined ? DEFAULT_LIST_TIMEOUTS_LIMIT : Math.floor(Number(args.limit));
20732
+ if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
20733
+ throw new PipelineExecutionError(spaceTrim$1(`
20734
+ Timeout \`limit\` must be a positive number.
20735
+ `));
20736
+ }
20737
+ if (parsedLimit > MAX_LIST_TIMEOUTS_LIMIT) {
20738
+ throw new PipelineExecutionError(spaceTrim$1(`
20739
+ Timeout \`limit\` must be at most \`${MAX_LIST_TIMEOUTS_LIMIT}\`.
20740
+ `));
20741
+ }
20742
+ return {
20743
+ includeFinished: args.includeFinished === true,
20744
+ limit: parsedLimit,
20745
+ };
20746
+ },
22626
20747
  };
22627
20748
 
22628
20749
  /**
@@ -22633,6 +20754,7 @@ const parseTimeoutToolArgs = {
22633
20754
  const TimeoutToolNames = {
22634
20755
  set: 'set_timeout',
22635
20756
  cancel: 'cancel_timeout',
20757
+ list: 'list_timeouts',
22636
20758
  };
22637
20759
 
22638
20760
  /**
@@ -22732,6 +20854,35 @@ function createTimeoutToolFunctions() {
22732
20854
  return JSON.stringify(result);
22733
20855
  }
22734
20856
  },
20857
+ async [TimeoutToolNames.list](args) {
20858
+ const runtimeContext = resolveTimeoutRuntimeContext(args);
20859
+ const { adapter, disabledResult } = getTimeoutToolRuntimeAdapterOrDisabledResult('list', runtimeContext);
20860
+ if (!adapter || disabledResult) {
20861
+ return JSON.stringify(disabledResult);
20862
+ }
20863
+ try {
20864
+ const parsedArgs = parseTimeoutToolArgs.list(args);
20865
+ const listedTimeouts = await adapter.listTimeouts(parsedArgs, runtimeContext);
20866
+ const result = {
20867
+ action: 'list',
20868
+ status: 'listed',
20869
+ items: listedTimeouts.items,
20870
+ total: listedTimeouts.total,
20871
+ };
20872
+ return createToolExecutionEnvelope({
20873
+ assistantMessage: listedTimeouts.total === 1 ? 'Found 1 timeout.' : `Found ${listedTimeouts.total} timeouts.`,
20874
+ toolResult: result,
20875
+ });
20876
+ }
20877
+ catch (error) {
20878
+ const result = {
20879
+ action: 'list',
20880
+ status: 'error',
20881
+ message: error instanceof Error ? error.message : String(error),
20882
+ };
20883
+ return JSON.stringify(result);
20884
+ }
20885
+ },
22735
20886
  };
22736
20887
  }
22737
20888
 
@@ -22765,26 +20916,45 @@ function createTimeoutTools(existingTools = []) {
22765
20916
  if (!tools.some((tool) => tool.name === TimeoutToolNames.cancel)) {
22766
20917
  tools.push({
22767
20918
  name: TimeoutToolNames.cancel,
22768
- description: 'Cancel one previously scheduled timeout in the current chat thread.',
20919
+ description: 'Cancel one previously scheduled timeout within the same user+agent scope, even if it was set in another chat.',
22769
20920
  parameters: {
22770
20921
  type: 'object',
22771
20922
  properties: {
22772
20923
  timeoutId: {
22773
20924
  type: 'string',
22774
- description: 'Identifier returned earlier by `set_timeout`.',
20925
+ description: 'Identifier returned earlier by `set_timeout` or `list_timeouts`.',
22775
20926
  },
22776
20927
  },
22777
20928
  required: ['timeoutId'],
22778
20929
  },
22779
20930
  });
22780
20931
  }
20932
+ if (!tools.some((tool) => tool.name === TimeoutToolNames.list)) {
20933
+ tools.push({
20934
+ name: TimeoutToolNames.list,
20935
+ description: 'List scheduled timeouts across all chats for this same user+agent scope so they can be reviewed or cancelled.',
20936
+ parameters: {
20937
+ type: 'object',
20938
+ properties: {
20939
+ includeFinished: {
20940
+ type: 'boolean',
20941
+ description: 'When true, include completed, failed, and cancelled rows in addition to active timeouts.',
20942
+ },
20943
+ limit: {
20944
+ type: 'number',
20945
+ description: 'Maximum number of rows to return (default 20, max 100).',
20946
+ },
20947
+ },
20948
+ },
20949
+ });
20950
+ }
22781
20951
  return tools;
22782
20952
  }
22783
20953
 
22784
20954
  /**
22785
20955
  * `USE TIMEOUT` commitment definition.
22786
20956
  *
22787
- * The `USE TIMEOUT` commitment enables thread-scoped timers that wake the same chat later.
20957
+ * The `USE TIMEOUT` commitment enables timeout wake-ups and scoped timeout management.
22788
20958
  *
22789
20959
  * @private [🪔] Maybe export the commitments through some package
22790
20960
  */
@@ -22799,7 +20969,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
22799
20969
  * Short one-line description of `USE TIMEOUT`.
22800
20970
  */
22801
20971
  get description() {
22802
- return 'Enable thread-scoped timers that can wake the same chat in the future.';
20972
+ return 'Enable timeout wake-ups plus scoped timeout listing/cancellation across chats.';
22803
20973
  }
22804
20974
  /**
22805
20975
  * Icon for this commitment.
@@ -22814,14 +20984,15 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
22814
20984
  return spaceTrim$1(`
22815
20985
  # USE TIMEOUT
22816
20986
 
22817
- Enables the agent to schedule thread-scoped timeout wake-ups.
20987
+ Enables timeout wake-ups and timeout management for the same user+agent scope.
22818
20988
 
22819
20989
  ## Key aspects
22820
20990
 
22821
20991
  - The agent uses \`set_timeout\` to schedule a future wake-up in the same chat thread.
22822
20992
  - The tool returns immediately while the timeout is stored and executed by the runtime later.
22823
20993
  - The wake-up arrives as a new user-like timeout message in the same conversation.
22824
- - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`.
20994
+ - The agent can inspect known timeouts via \`list_timeouts\`.
20995
+ - The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`, including timeouts created in another chat.
22825
20996
  - Commitment content is treated as optional timeout policy instructions.
22826
20997
 
22827
20998
  ## Examples
@@ -22850,6 +21021,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
22850
21021
  return {
22851
21022
  [TimeoutToolNames.set]: 'Set timer',
22852
21023
  [TimeoutToolNames.cancel]: 'Cancel timer',
21024
+ [TimeoutToolNames.list]: 'List timers',
22853
21025
  };
22854
21026
  }
22855
21027
  /**
@@ -23703,7 +21875,7 @@ function getAllCommitmentsToolFunctionsForNode() {
23703
21875
  function extractOneBlockFromMarkdown(markdown) {
23704
21876
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
23705
21877
  if (codeBlocks.length !== 1) {
23706
- throw new ParseError(spaceTrim$2((block) => `
21878
+ throw new ParseError(spaceTrim$1((block) => `
23707
21879
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
23708
21880
 
23709
21881
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -23812,7 +21984,7 @@ class JavascriptEvalExecutionTools {
23812
21984
  }
23813
21985
  // Note: [💎]
23814
21986
  // Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
23815
- const spaceTrim = (_) => spaceTrim$2(_);
21987
+ const spaceTrim = (_) => _spaceTrim(_);
23816
21988
  $preserve(spaceTrim);
23817
21989
  const removeQuotes$1 = removeQuotes;
23818
21990
  $preserve(removeQuotes$1);
@@ -23903,7 +22075,7 @@ class JavascriptEvalExecutionTools {
23903
22075
  .join('\n');
23904
22076
  // script = templateParameters(script, parameters);
23905
22077
  // <- TODO: [🧠][🥳] Should be this is one of two variants how to use parameters in script
23906
- const statementToEvaluate = spaceTrim$2((block) => `
22078
+ const statementToEvaluate = _spaceTrim((block) => `
23907
22079
 
23908
22080
  // Build-in functions:
23909
22081
  ${block(buildinFunctionsStatement)}
@@ -23918,7 +22090,7 @@ class JavascriptEvalExecutionTools {
23918
22090
  (async ()=>{ ${script} })()
23919
22091
  `);
23920
22092
  if (this.options.isVerbose) {
23921
- console.info(spaceTrim$2((block) => `
22093
+ console.info(_spaceTrim((block) => `
23922
22094
  🚀 Evaluating ${scriptLanguage} script:
23923
22095
 
23924
22096
  ${block(statementToEvaluate)}`));
@@ -23927,7 +22099,7 @@ class JavascriptEvalExecutionTools {
23927
22099
  try {
23928
22100
  result = await eval(statementToEvaluate);
23929
22101
  if (this.options.isVerbose) {
23930
- console.info(spaceTrim$2((block) => `
22102
+ console.info(_spaceTrim((block) => `
23931
22103
  🚀 Script evaluated successfully, result:
23932
22104
  ${block(valueToString(result))}
23933
22105
  `));
@@ -23946,7 +22118,7 @@ class JavascriptEvalExecutionTools {
23946
22118
  To: [PipelineExecutionError: Parameter `{thing}` is not defined],
23947
22119
  */
23948
22120
  if (!statementToEvaluate.includes(undefinedName + '(')) {
23949
- throw new PipelineExecutionError(spaceTrim$2((block) => `
22121
+ throw new PipelineExecutionError(_spaceTrim((block) => `
23950
22122
 
23951
22123
  Parameter \`{${undefinedName}}\` is not defined
23952
22124
 
@@ -23968,7 +22140,7 @@ class JavascriptEvalExecutionTools {
23968
22140
  `));
23969
22141
  }
23970
22142
  else {
23971
- throw new PipelineExecutionError(spaceTrim$2((block) => `
22143
+ throw new PipelineExecutionError(_spaceTrim((block) => `
23972
22144
  Function ${undefinedName}() is not defined
23973
22145
 
23974
22146
  - Make sure that the function is one of built-in functions
@@ -25325,7 +23497,7 @@ function validateBook(source) {
25325
23497
  * @deprecated Use `$generateBookBoilerplate` instead
25326
23498
  * @public exported from `@promptbook/core`
25327
23499
  */
25328
- padBook(validateBook(spaceTrim$2(`
23500
+ padBook(validateBook(spaceTrim$1(`
25329
23501
  AI Avatar
25330
23502
 
25331
23503
  PERSONA A friendly AI assistant that helps you with your tasks
@@ -25374,7 +23546,7 @@ function book(strings, ...values) {
25374
23546
  const bookString = prompt(strings, ...values).toString();
25375
23547
  if (!isValidPipelineString(bookString)) {
25376
23548
  // TODO: Make the CustomError for this
25377
- throw new Error(spaceTrim$2(`
23549
+ throw new Error(spaceTrim$1(`
25378
23550
  The string is not a valid pipeline string
25379
23551
 
25380
23552
  book\`
@@ -25384,7 +23556,7 @@ function book(strings, ...values) {
25384
23556
  }
25385
23557
  if (!isValidBook(bookString)) {
25386
23558
  // TODO: Make the CustomError for this
25387
- throw new Error(spaceTrim$2(`
23559
+ throw new Error(spaceTrim$1(`
25388
23560
  The string is not a valid book
25389
23561
 
25390
23562
  book\`
@@ -25668,7 +23840,7 @@ function computeAgentHash(agentSource) {
25668
23840
  * @public exported from `@promptbook/core`
25669
23841
  */
25670
23842
  function normalizeAgentName(rawAgentName) {
25671
- return titleToName(spaceTrim$2(rawAgentName));
23843
+ return titleToName(spaceTrim$1(rawAgentName));
25672
23844
  }
25673
23845
 
25674
23846
  /**
@@ -26112,7 +24284,7 @@ function parseAgentSource(agentSource) {
26112
24284
  continue;
26113
24285
  }
26114
24286
  if (commitment.type === 'FROM') {
26115
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
24287
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
26116
24288
  if (content === 'Adam' || content === '' /* <- Note: Adam is implicit */) {
26117
24289
  continue;
26118
24290
  }
@@ -26135,7 +24307,7 @@ function parseAgentSource(agentSource) {
26135
24307
  continue;
26136
24308
  }
26137
24309
  if (commitment.type === 'IMPORT') {
26138
- const content = spaceTrim$2(commitment.content).split(/\r?\n/)[0] || '';
24310
+ const content = spaceTrim$1(commitment.content).split(/\r?\n/)[0] || '';
26139
24311
  let label = content;
26140
24312
  let iconName = 'ExternalLink'; // Import remote
26141
24313
  try {
@@ -26173,7 +24345,7 @@ function parseAgentSource(agentSource) {
26173
24345
  continue;
26174
24346
  }
26175
24347
  if (commitment.type === 'KNOWLEDGE') {
26176
- const content = spaceTrim$2(commitment.content);
24348
+ const content = spaceTrim$1(commitment.content);
26177
24349
  const extractedUrls = extractUrlsFromText(content);
26178
24350
  let label = content;
26179
24351
  let iconName = 'Book';
@@ -26232,7 +24404,7 @@ function parseAgentSource(agentSource) {
26232
24404
  continue;
26233
24405
  }
26234
24406
  if (commitment.type === 'META LINK') {
26235
- const linkValue = spaceTrim$2(commitment.content);
24407
+ const linkValue = spaceTrim$1(commitment.content);
26236
24408
  links.push(linkValue);
26237
24409
  meta.link = linkValue;
26238
24410
  continue;
@@ -26242,11 +24414,11 @@ function parseAgentSource(agentSource) {
26242
24414
  continue;
26243
24415
  }
26244
24416
  if (commitment.type === 'META IMAGE') {
26245
- meta.image = spaceTrim$2(commitment.content);
24417
+ meta.image = spaceTrim$1(commitment.content);
26246
24418
  continue;
26247
24419
  }
26248
24420
  if (commitment.type === 'META DESCRIPTION') {
26249
- meta.description = spaceTrim$2(commitment.content);
24421
+ meta.description = spaceTrim$1(commitment.content);
26250
24422
  continue;
26251
24423
  }
26252
24424
  if (commitment.type === 'META DISCLAIMER') {
@@ -26254,7 +24426,7 @@ function parseAgentSource(agentSource) {
26254
24426
  continue;
26255
24427
  }
26256
24428
  if (commitment.type === 'META INPUT PLACEHOLDER') {
26257
- meta.inputPlaceholder = spaceTrim$2(commitment.content);
24429
+ meta.inputPlaceholder = spaceTrim$1(commitment.content);
26258
24430
  continue;
26259
24431
  }
26260
24432
  if (commitment.type === 'MESSAGE SUFFIX') {
@@ -26270,7 +24442,7 @@ function parseAgentSource(agentSource) {
26270
24442
  continue;
26271
24443
  }
26272
24444
  if (commitment.type === 'META VOICE') {
26273
- meta.voice = spaceTrim$2(commitment.content);
24445
+ meta.voice = spaceTrim$1(commitment.content);
26274
24446
  continue;
26275
24447
  }
26276
24448
  if (commitment.type !== 'META') {
@@ -26279,10 +24451,10 @@ function parseAgentSource(agentSource) {
26279
24451
  // Parse META commitments - format is "META TYPE content"
26280
24452
  const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
26281
24453
  if (metaTypeRaw === 'LINK') {
26282
- links.push(spaceTrim$2(commitment.content.substring(metaTypeRaw.length)));
24454
+ links.push(spaceTrim$1(commitment.content.substring(metaTypeRaw.length)));
26283
24455
  }
26284
24456
  const metaType = normalizeTo_camelCase(metaTypeRaw);
26285
- meta[metaType] = spaceTrim$2(commitment.content.substring(metaTypeRaw.length));
24457
+ meta[metaType] = spaceTrim$1(commitment.content.substring(metaTypeRaw.length));
26286
24458
  }
26287
24459
  // Generate fullname fallback if no meta fullname specified
26288
24460
  if (!meta.fullname) {
@@ -26313,7 +24485,7 @@ function parseAgentSource(agentSource) {
26313
24485
  * @returns The content with normalized separators
26314
24486
  */
26315
24487
  function normalizeSeparator(content) {
26316
- const trimmed = spaceTrim$2(content);
24488
+ const trimmed = spaceTrim$1(content);
26317
24489
  if (trimmed.includes(',')) {
26318
24490
  return trimmed;
26319
24491
  }
@@ -26326,7 +24498,7 @@ function normalizeSeparator(content) {
26326
24498
  * @returns Normalized domain or a trimmed fallback.
26327
24499
  */
26328
24500
  function normalizeMetaDomain(content) {
26329
- const trimmed = spaceTrim$2(content);
24501
+ const trimmed = spaceTrim$1(content);
26330
24502
  return normalizeDomainForMatching(trimmed) || trimmed.toLowerCase();
26331
24503
  }
26332
24504
  /**
@@ -27586,7 +25758,7 @@ function pricing(value) {
27586
25758
  /**
27587
25759
  * List of available OpenAI models with pricing
27588
25760
  *
27589
- * Note: Synced with official API docs at 2025-11-19
25761
+ * Note: Synced with official API docs at 2026-03-22
27590
25762
  *
27591
25763
  * @see https://platform.openai.com/docs/models/
27592
25764
  * @see https://openai.com/api/pricing/
@@ -27708,8 +25880,8 @@ const OPENAI_MODELS = exportJson({
27708
25880
  modelName: 'gpt-4.1',
27709
25881
  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.',
27710
25882
  pricing: {
27711
- prompt: pricing(`$3.00 / 1M tokens`),
27712
- output: pricing(`$12.00 / 1M tokens`),
25883
+ prompt: pricing(`$2.00 / 1M tokens`),
25884
+ output: pricing(`$8.00 / 1M tokens`),
27713
25885
  },
27714
25886
  },
27715
25887
  /**/
@@ -27720,8 +25892,8 @@ const OPENAI_MODELS = exportJson({
27720
25892
  modelName: 'gpt-4.1-mini',
27721
25893
  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.',
27722
25894
  pricing: {
27723
- prompt: pricing(`$0.80 / 1M tokens`),
27724
- output: pricing(`$3.20 / 1M tokens`),
25895
+ prompt: pricing(`$0.40 / 1M tokens`),
25896
+ output: pricing(`$1.60 / 1M tokens`),
27725
25897
  },
27726
25898
  },
27727
25899
  /**/
@@ -27732,8 +25904,8 @@ const OPENAI_MODELS = exportJson({
27732
25904
  modelName: 'gpt-4.1-nano',
27733
25905
  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.',
27734
25906
  pricing: {
27735
- prompt: pricing(`$0.20 / 1M tokens`),
27736
- output: pricing(`$0.80 / 1M tokens`),
25907
+ prompt: pricing(`$0.10 / 1M tokens`),
25908
+ output: pricing(`$0.40 / 1M tokens`),
27737
25909
  },
27738
25910
  },
27739
25911
  /**/
@@ -27744,8 +25916,8 @@ const OPENAI_MODELS = exportJson({
27744
25916
  modelName: 'o3',
27745
25917
  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.',
27746
25918
  pricing: {
27747
- prompt: pricing(`$15.00 / 1M tokens`),
27748
- output: pricing(`$60.00 / 1M tokens`),
25919
+ prompt: pricing(`$2.00 / 1M tokens`),
25920
+ output: pricing(`$8.00 / 1M tokens`),
27749
25921
  },
27750
25922
  },
27751
25923
  /**/
@@ -27756,8 +25928,8 @@ const OPENAI_MODELS = exportJson({
27756
25928
  modelName: 'o3-pro',
27757
25929
  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.',
27758
25930
  pricing: {
27759
- prompt: pricing(`$30.00 / 1M tokens`),
27760
- output: pricing(`$120.00 / 1M tokens`),
25931
+ prompt: pricing(`$20.00 / 1M tokens`),
25932
+ output: pricing(`$80.00 / 1M tokens`),
27761
25933
  },
27762
25934
  },
27763
25935
  /**/
@@ -27768,8 +25940,8 @@ const OPENAI_MODELS = exportJson({
27768
25940
  modelName: 'o4-mini',
27769
25941
  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.',
27770
25942
  pricing: {
27771
- prompt: pricing(`$4.00 / 1M tokens`),
27772
- output: pricing(`$16.00 / 1M tokens`),
25943
+ prompt: pricing(`$1.10 / 1M tokens`),
25944
+ output: pricing(`$4.40 / 1M tokens`),
27773
25945
  },
27774
25946
  },
27775
25947
  /**/
@@ -28127,8 +26299,8 @@ const OPENAI_MODELS = exportJson({
28127
26299
  modelName: 'gpt-4o-2024-05-13',
28128
26300
  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.',
28129
26301
  pricing: {
28130
- prompt: pricing(`$5.00 / 1M tokens`),
28131
- output: pricing(`$15.00 / 1M tokens`),
26302
+ prompt: pricing(`$2.50 / 1M tokens`),
26303
+ output: pricing(`$10.00 / 1M tokens`),
28132
26304
  },
28133
26305
  },
28134
26306
  /**/
@@ -28139,8 +26311,8 @@ const OPENAI_MODELS = exportJson({
28139
26311
  modelName: 'gpt-4o',
28140
26312
  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.",
28141
26313
  pricing: {
28142
- prompt: pricing(`$5.00 / 1M tokens`),
28143
- output: pricing(`$15.00 / 1M tokens`),
26314
+ prompt: pricing(`$2.50 / 1M tokens`),
26315
+ output: pricing(`$10.00 / 1M tokens`),
28144
26316
  },
28145
26317
  },
28146
26318
  /**/
@@ -28211,8 +26383,8 @@ const OPENAI_MODELS = exportJson({
28211
26383
  modelName: 'o3-mini',
28212
26384
  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.',
28213
26385
  pricing: {
28214
- prompt: pricing(`$3.00 / 1M tokens`),
28215
- output: pricing(`$12.00 / 1M tokens`),
26386
+ prompt: pricing(`$1.10 / 1M tokens`),
26387
+ output: pricing(`$4.40 / 1M tokens`),
28216
26388
  },
28217
26389
  },
28218
26390
  /**/
@@ -28312,53 +26484,6 @@ resultContent, rawResponse, duration = ZERO_VALUE) {
28312
26484
  * TODO: [🤝] DRY Maybe some common abstraction between `computeOpenAiUsage` and `computeAnthropicClaudeUsage`
28313
26485
  */
28314
26486
 
28315
- /**
28316
- * Maps Promptbook tools to OpenAI tools.
28317
- *
28318
- * @private
28319
- */
28320
- function mapToolsToOpenAi(tools) {
28321
- return tools.map((tool) => ({
28322
- type: 'function',
28323
- function: {
28324
- name: tool.name,
28325
- description: tool.description,
28326
- parameters: tool.parameters,
28327
- },
28328
- }));
28329
- }
28330
-
28331
- /**
28332
- * Builds a tool invocation script that injects hidden runtime context into tool args.
28333
- *
28334
- * @private utility of OpenAI tool execution wrappers
28335
- */
28336
- function buildToolInvocationScript(options) {
28337
- const { functionName, functionArgsExpression } = options;
28338
- return `
28339
- const args = ${functionArgsExpression};
28340
- const runtimeContextRaw =
28341
- typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
28342
- ? undefined
28343
- : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
28344
-
28345
- if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
28346
- args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
28347
- }
28348
-
28349
- const toolProgressTokenRaw =
28350
- typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
28351
- ? undefined
28352
- : ${TOOL_PROGRESS_TOKEN_PARAMETER};
28353
-
28354
- if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
28355
- args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
28356
- }
28357
-
28358
- return await ${functionName}(args);
28359
- `;
28360
- }
28361
-
28362
26487
  /**
28363
26488
  * Parses an OpenAI error message to identify which parameter is unsupported
28364
26489
  *
@@ -28415,6 +26540,53 @@ function isUnsupportedParameterError(error) {
28415
26540
  errorMessage.includes('does not support'));
28416
26541
  }
28417
26542
 
26543
+ /**
26544
+ * Builds a tool invocation script that injects hidden runtime context into tool args.
26545
+ *
26546
+ * @private utility of OpenAI tool execution wrappers
26547
+ */
26548
+ function buildToolInvocationScript(options) {
26549
+ const { functionName, functionArgsExpression } = options;
26550
+ return `
26551
+ const args = ${functionArgsExpression};
26552
+ const runtimeContextRaw =
26553
+ typeof ${TOOL_RUNTIME_CONTEXT_PARAMETER} === 'undefined'
26554
+ ? undefined
26555
+ : ${TOOL_RUNTIME_CONTEXT_PARAMETER};
26556
+
26557
+ if (runtimeContextRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
26558
+ args.${TOOL_RUNTIME_CONTEXT_ARGUMENT} = runtimeContextRaw;
26559
+ }
26560
+
26561
+ const toolProgressTokenRaw =
26562
+ typeof ${TOOL_PROGRESS_TOKEN_PARAMETER} === 'undefined'
26563
+ ? undefined
26564
+ : ${TOOL_PROGRESS_TOKEN_PARAMETER};
26565
+
26566
+ if (toolProgressTokenRaw !== undefined && args && typeof args === 'object' && !Array.isArray(args)) {
26567
+ args.${TOOL_PROGRESS_TOKEN_ARGUMENT} = toolProgressTokenRaw;
26568
+ }
26569
+
26570
+ return await ${functionName}(args);
26571
+ `;
26572
+ }
26573
+
26574
+ /**
26575
+ * Maps Promptbook tools to OpenAI tools.
26576
+ *
26577
+ * @private
26578
+ */
26579
+ function mapToolsToOpenAi(tools) {
26580
+ return tools.map((tool) => ({
26581
+ type: 'function',
26582
+ function: {
26583
+ name: tool.name,
26584
+ description: tool.description,
26585
+ parameters: tool.parameters,
26586
+ },
26587
+ }));
26588
+ }
26589
+
28418
26590
  /**
28419
26591
  * Provides access to the structured clone implementation when available.
28420
26592
  */
@@ -29381,7 +27553,7 @@ class OpenAiCompatibleExecutionTools {
29381
27553
  // Note: Match exact or prefix for model families
29382
27554
  const model = this.HARDCODED_MODELS.find(({ modelName }) => modelName === defaultModelName || modelName.startsWith(defaultModelName));
29383
27555
  if (model === undefined) {
29384
- throw new PipelineExecutionError(spaceTrim$2((block) => `
27556
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
29385
27557
  Cannot find model in ${this.title} models with name "${defaultModelName}" which should be used as default.
29386
27558
 
29387
27559
  Available models:
@@ -30307,7 +28479,7 @@ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
30307
28479
  }
30308
28480
  }
30309
28481
 
30310
- const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5-mini-2025-08-07';
28482
+ const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-nano';
30311
28483
  /**
30312
28484
  * Creates one structured log entry for streamed tool-call updates.
30313
28485
  *
@@ -30802,7 +28974,7 @@ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
30802
28974
  }),
30803
28975
  ],
30804
28976
  };
30805
- const errorMessage = spaceTrim$2((block) => `
28977
+ const errorMessage = spaceTrim$1((block) => `
30806
28978
 
30807
28979
  The invoked tool \`${functionName}\` failed with error:
30808
28980
 
@@ -31520,7 +29692,7 @@ class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
31520
29692
  assertsError(error);
31521
29693
  const serializedError = serializeError(error);
31522
29694
  errors = [serializedError];
31523
- functionResponse = spaceTrim$2((block) => `
29695
+ functionResponse = spaceTrim$1((block) => `
31524
29696
 
31525
29697
  The invoked tool \`${functionName}\` failed with error:
31526
29698
 
@@ -32528,7 +30700,7 @@ class SelfLearningManager {
32528
30700
  if (isJsonSchemaResponseFormat(responseFormat)) {
32529
30701
  const jsonSchema = responseFormat.json_schema;
32530
30702
  const schemaJson = JSON.stringify(jsonSchema, null, 4);
32531
- userMessageContent = spaceTrim$2((block) => `
30703
+ userMessageContent = spaceTrim$1((block) => `
32532
30704
  ${block(prompt.content)}
32533
30705
 
32534
30706
  NOTE Request was made through OpenAI Compatible API with \`response_format\` of type \`json_schema\` with the following schema:
@@ -32559,12 +30731,12 @@ class SelfLearningManager {
32559
30731
  const formattedAgentMessage = formatAgentMessageForJsonMode(result.content, usesJsonSchemaMode);
32560
30732
  const teacherInstructions = extractOpenTeacherInstructions(agentSource);
32561
30733
  const teacherInstructionsSection = teacherInstructions
32562
- ? spaceTrim$2((block) => `
30734
+ ? spaceTrim$1((block) => `
32563
30735
  **Teacher instructions:**
32564
30736
  ${block(teacherInstructions)}
32565
30737
  `)
32566
30738
  : '';
32567
- const teacherPromptContent = spaceTrim$2((block) => `
30739
+ const teacherPromptContent = spaceTrim$1((block) => `
32568
30740
 
32569
30741
  You are a teacher agent helping another agent to learn from its interactions.
32570
30742
 
@@ -32597,7 +30769,7 @@ class SelfLearningManager {
32597
30769
  ? '- This interaction used JSON mode, so the agent answer should stay as a formatted JSON code block.'
32598
30770
  : ''}
32599
30771
  ${block(isInitialMessageMissing
32600
- ? spaceTrim$2(`
30772
+ ? spaceTrim$1(`
32601
30773
  - The agent source does not have an INITIAL MESSAGE defined, generate one.
32602
30774
  - 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.
32603
30775
  - The quick option looks like \`[👋 Hello](?message=Hello, how are you?)\`
@@ -32640,7 +30812,7 @@ class SelfLearningManager {
32640
30812
  */
32641
30813
  appendToAgentSource(section) {
32642
30814
  const currentSource = this.options.getAgentSource();
32643
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + section));
30815
+ const newSource = padBook(validateBook(spaceTrim$1(currentSource) + section));
32644
30816
  this.options.updateAgentSource(newSource);
32645
30817
  }
32646
30818
  }
@@ -32668,13 +30840,13 @@ function formatAgentMessageForJsonMode(content, isJsonMode) {
32668
30840
  }
32669
30841
  const parsedJson = tryParseJson(content);
32670
30842
  if (parsedJson === null) {
32671
- return spaceTrim$2((block) => `
30843
+ return spaceTrim$1((block) => `
32672
30844
  \`\`\`json
32673
30845
  ${block(content)}
32674
30846
  \`\`\`
32675
30847
  `);
32676
30848
  }
32677
- return spaceTrim$2((block) => `
30849
+ return spaceTrim$1((block) => `
32678
30850
  \`\`\`json
32679
30851
  ${block(JSON.stringify(parsedJson, null, 4))}
32680
30852
  \`\`\`
@@ -32706,7 +30878,7 @@ function formatSelfLearningSample(options) {
32706
30878
  const internalMessagesSection = options.internalMessages
32707
30879
  .map((internalMessage) => formatInternalLearningMessage(internalMessage))
32708
30880
  .join('\n\n');
32709
- return spaceTrim$2((block) => `
30881
+ return spaceTrim$1((block) => `
32710
30882
 
32711
30883
  USER MESSAGE
32712
30884
  ${block(options.userMessageContent)}
@@ -32724,7 +30896,7 @@ function formatSelfLearningSample(options) {
32724
30896
  * @private function of Agent
32725
30897
  */
32726
30898
  function formatInternalLearningMessage(internalMessage) {
32727
- return spaceTrim$2((block) => `
30899
+ return spaceTrim$1((block) => `
32728
30900
  INTERNAL MESSAGE
32729
30901
  ${block(stringifyInternalLearningPayload(internalMessage))}
32730
30902
  `);
@@ -33190,7 +31362,7 @@ function buildRemoteAgentSource(profile, meta) {
33190
31362
  .filter((line) => Boolean(line))
33191
31363
  .join('\n');
33192
31364
  const personaBlock = profile.personaDescription
33193
- ? spaceTrim$2((block) => `
31365
+ ? spaceTrim$1((block) => `
33194
31366
  PERSONA
33195
31367
  ${block(profile.personaDescription || '')}
33196
31368
  `)
@@ -33226,7 +31398,7 @@ class RemoteAgent extends Agent {
33226
31398
  // <- TODO: [🐱‍🚀] What about closed-source agents?
33227
31399
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
33228
31400
  if (!profileResponse.ok) {
33229
- throw new Error(spaceTrim$2((block) => `
31401
+ throw new Error(spaceTrim$1((block) => `
33230
31402
  Failed to fetch remote agent profile:
33231
31403
 
33232
31404
  Agent URL: