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