@promptbook/cli 0.112.0-12 â 0.112.0-13
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 +260 -2072
- package/esm/index.es.js.map +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/version.d.ts +1 -1
- package/package.json +1 -2
- package/umd/index.umd.js +263 -2072
- package/umd/index.umd.js.map +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/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
|
@@ -14,13 +14,10 @@ import sha256 from 'crypto-js/sha256';
|
|
|
14
14
|
import { io } from 'socket.io-client';
|
|
15
15
|
import { SHA256 } from 'crypto-js';
|
|
16
16
|
import JSZip from 'jszip';
|
|
17
|
-
import { randomBytes,
|
|
17
|
+
import { randomBytes, createHash } from 'crypto';
|
|
18
18
|
import { Readability } from '@mozilla/readability';
|
|
19
19
|
import { JSDOM } from 'jsdom';
|
|
20
20
|
import { Converter } from 'showdown';
|
|
21
|
-
import { tmpdir } from 'os';
|
|
22
|
-
import { ConfigChecker } from 'configchecker';
|
|
23
|
-
import { chromium } from 'playwright';
|
|
24
21
|
import glob from 'glob-promise';
|
|
25
22
|
import moment from 'moment';
|
|
26
23
|
import express from 'express';
|
|
@@ -60,7 +57,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
60
57
|
* @generated
|
|
61
58
|
* @see https://github.com/webgptorg/promptbook
|
|
62
59
|
*/
|
|
63
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
60
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-13';
|
|
64
61
|
/**
|
|
65
62
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
66
63
|
* Note: [ð] Ignore a discrepancy between file name and entity name
|
|
@@ -16326,2071 +16323,61 @@ async function fetchUrlContent(url) {
|
|
|
16326
16323
|
*/
|
|
16327
16324
|
|
|
16328
16325
|
/**
|
|
16329
|
-
*
|
|
16330
|
-
*
|
|
16331
|
-
* @private internal runtime wiring for commitment tools
|
|
16332
|
-
*/
|
|
16333
|
-
const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
|
|
16334
|
-
/**
|
|
16335
|
-
* Hidden argument key used to pass runtime context into individual tool calls.
|
|
16336
|
-
*
|
|
16337
|
-
* @private internal runtime wiring for commitment tools
|
|
16338
|
-
*/
|
|
16339
|
-
const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
|
|
16340
|
-
/**
|
|
16341
|
-
* Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
|
|
16342
|
-
*
|
|
16343
|
-
* @private internal runtime wiring for commitment tools
|
|
16344
|
-
*/
|
|
16345
|
-
const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
|
|
16346
|
-
/**
|
|
16347
|
-
* Hidden argument key used to pass a tool-progress listener token into individual tool calls.
|
|
16348
|
-
*
|
|
16349
|
-
* @private internal runtime wiring for commitment tools
|
|
16350
|
-
*/
|
|
16351
|
-
const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
|
|
16352
|
-
/**
|
|
16353
|
-
* Monotonic counter used for hidden progress-listener tokens.
|
|
16354
|
-
*
|
|
16355
|
-
* @private internal runtime wiring for commitment tools
|
|
16356
|
-
*/
|
|
16357
|
-
let toolCallProgressListenerCounter = 0;
|
|
16358
|
-
/**
|
|
16359
|
-
* Active tool-progress listeners keyed by hidden execution token.
|
|
16360
|
-
*
|
|
16361
|
-
* @private internal runtime wiring for commitment tools
|
|
16362
|
-
*/
|
|
16363
|
-
const toolCallProgressListeners = new Map();
|
|
16364
|
-
/**
|
|
16365
|
-
* Parses unknown runtime context payload into a normalized object.
|
|
16366
|
-
*
|
|
16367
|
-
* @private internal runtime wiring for commitment tools
|
|
16368
|
-
*/
|
|
16369
|
-
function parseToolRuntimeContext(rawValue) {
|
|
16370
|
-
if (!rawValue) {
|
|
16371
|
-
return null;
|
|
16372
|
-
}
|
|
16373
|
-
let parsed = rawValue;
|
|
16374
|
-
if (typeof rawValue === 'string') {
|
|
16375
|
-
try {
|
|
16376
|
-
parsed = JSON.parse(rawValue);
|
|
16377
|
-
}
|
|
16378
|
-
catch (_a) {
|
|
16379
|
-
return null;
|
|
16380
|
-
}
|
|
16381
|
-
}
|
|
16382
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
16383
|
-
return null;
|
|
16384
|
-
}
|
|
16385
|
-
return parsed;
|
|
16386
|
-
}
|
|
16387
|
-
/**
|
|
16388
|
-
* Reads runtime context attached to tool call arguments.
|
|
16389
|
-
*
|
|
16390
|
-
* @private internal runtime wiring for commitment tools
|
|
16391
|
-
*/
|
|
16392
|
-
function readToolRuntimeContextFromToolArgs(args) {
|
|
16393
|
-
return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
|
|
16394
|
-
}
|
|
16395
|
-
/**
|
|
16396
|
-
* Reads the hidden tool-progress token from tool arguments.
|
|
16397
|
-
*
|
|
16398
|
-
* @private internal runtime wiring for commitment tools
|
|
16399
|
-
*/
|
|
16400
|
-
function readToolProgressTokenFromToolArgs(args) {
|
|
16401
|
-
const token = args[TOOL_PROGRESS_TOKEN_ARGUMENT];
|
|
16402
|
-
return typeof token === 'string' && token.trim().length > 0 ? token : null;
|
|
16403
|
-
}
|
|
16404
|
-
/**
|
|
16405
|
-
* Serializes runtime context for prompt parameters.
|
|
16406
|
-
*
|
|
16407
|
-
* @private internal runtime wiring for commitment tools
|
|
16408
|
-
*/
|
|
16409
|
-
function serializeToolRuntimeContext(context) {
|
|
16410
|
-
return JSON.stringify(context);
|
|
16411
|
-
}
|
|
16412
|
-
/**
|
|
16413
|
-
* Registers one in-memory listener that receives progress updates emitted by a running tool.
|
|
16414
|
-
*
|
|
16415
|
-
* The returned token is passed into script execution as a hidden argument so tool implementations
|
|
16416
|
-
* can stream progress without exposing extra parameters to the model.
|
|
16417
|
-
*
|
|
16418
|
-
* @param listener - Listener notified about tool progress.
|
|
16419
|
-
* @returns Hidden token used to route progress updates.
|
|
16420
|
-
* @private internal runtime wiring for commitment tools
|
|
16421
|
-
*/
|
|
16422
|
-
function registerToolCallProgressListener(listener) {
|
|
16423
|
-
toolCallProgressListenerCounter += 1;
|
|
16424
|
-
const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
|
|
16425
|
-
toolCallProgressListeners.set(token, listener);
|
|
16426
|
-
return token;
|
|
16427
|
-
}
|
|
16428
|
-
/**
|
|
16429
|
-
* Unregisters one in-memory progress listener.
|
|
16430
|
-
*
|
|
16431
|
-
* @param token - Token previously created by `registerToolCallProgressListener`.
|
|
16432
|
-
* @private internal runtime wiring for commitment tools
|
|
16433
|
-
*/
|
|
16434
|
-
function unregisterToolCallProgressListener(token) {
|
|
16435
|
-
toolCallProgressListeners.delete(token);
|
|
16436
|
-
}
|
|
16437
|
-
/**
|
|
16438
|
-
* Emits one tool progress update using a hidden token carried in tool arguments.
|
|
16439
|
-
*
|
|
16440
|
-
* @param args - Raw tool arguments including hidden runtime keys.
|
|
16441
|
-
* @param update - Incremental progress update.
|
|
16442
|
-
* @returns `true` when a listener was found and notified.
|
|
16443
|
-
* @private internal runtime wiring for commitment tools
|
|
16444
|
-
*/
|
|
16445
|
-
function emitToolCallProgressFromToolArgs(args, update) {
|
|
16446
|
-
const token = readToolProgressTokenFromToolArgs(args);
|
|
16447
|
-
if (!token) {
|
|
16448
|
-
return false;
|
|
16449
|
-
}
|
|
16450
|
-
const listener = toolCallProgressListeners.get(token);
|
|
16451
|
-
if (!listener) {
|
|
16452
|
-
return false;
|
|
16453
|
-
}
|
|
16454
|
-
listener(update);
|
|
16455
|
-
return true;
|
|
16456
|
-
}
|
|
16457
|
-
/**
|
|
16458
|
-
* Note: [ð] Ignore a discrepancy between file name and entity name
|
|
16459
|
-
*/
|
|
16460
|
-
|
|
16461
|
-
/**
|
|
16462
|
-
* Logical public directory marker used in `run_browser` payload paths.
|
|
16463
|
-
*
|
|
16464
|
-
* This value is kept stable for UI parsing and `/api/browser-artifacts/*` URL mapping.
|
|
16465
|
-
*/
|
|
16466
|
-
const RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY = '.playwright-cli';
|
|
16467
|
-
/**
|
|
16468
|
-
* Runtime environment variable that overrides local artifact storage directory.
|
|
16469
|
-
*/
|
|
16470
|
-
const RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV = 'RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY';
|
|
16471
|
-
/**
|
|
16472
|
-
* Default writable directory for `run_browser` screenshot/video artifacts.
|
|
16473
|
-
*/
|
|
16474
|
-
const DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY = join(tmpdir(), 'promptbook', 'run-browser-artifacts');
|
|
16475
|
-
/**
|
|
16476
|
-
* Converts Windows separators to POSIX separators for payload paths.
|
|
16477
|
-
*/
|
|
16478
|
-
function toPosixPath$1(pathname) {
|
|
16479
|
-
return pathname.split('\\').join('/');
|
|
16480
|
-
}
|
|
16481
|
-
/**
|
|
16482
|
-
* Resolves writable filesystem directory used for artifact persistence.
|
|
16483
|
-
*/
|
|
16484
|
-
function resolveRunBrowserArtifactStorageDirectory() {
|
|
16485
|
-
const configuredStorageDirectory = process.env[RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY_ENV];
|
|
16486
|
-
if (configuredStorageDirectory && configuredStorageDirectory.trim()) {
|
|
16487
|
-
return configuredStorageDirectory.trim();
|
|
16488
|
-
}
|
|
16489
|
-
return DEFAULT_RUN_BROWSER_ARTIFACT_STORAGE_DIRECTORY;
|
|
16490
|
-
}
|
|
16491
|
-
/**
|
|
16492
|
-
* Resolves absolute filesystem path of one artifact filename.
|
|
16493
|
-
*/
|
|
16494
|
-
function resolveRunBrowserArtifactFilesystemPath(artifactFilename) {
|
|
16495
|
-
return join(resolveRunBrowserArtifactStorageDirectory(), artifactFilename);
|
|
16496
|
-
}
|
|
16497
|
-
/**
|
|
16498
|
-
* Resolves payload path of one artifact filename used by replay renderers.
|
|
16499
|
-
*/
|
|
16500
|
-
function resolveRunBrowserArtifactPublicPath(artifactFilename) {
|
|
16501
|
-
return toPosixPath$1(`${RUN_BROWSER_ARTIFACT_PUBLIC_DIRECTORY}/${artifactFilename}`);
|
|
16502
|
-
}
|
|
16503
|
-
|
|
16504
|
-
/**
|
|
16505
|
-
* Error code used for remote-browser infrastructure outages.
|
|
16506
|
-
*/
|
|
16507
|
-
const REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE = 'REMOTE_BROWSER_UNAVAILABLE';
|
|
16508
|
-
/**
|
|
16509
|
-
* Error thrown when a remote Playwright browser cannot be reached.
|
|
16510
|
-
*/
|
|
16511
|
-
class RemoteBrowserUnavailableError extends KnowledgeScrapeError {
|
|
16512
|
-
constructor(options) {
|
|
16513
|
-
var _a;
|
|
16514
|
-
super(options.message);
|
|
16515
|
-
this.code = REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
|
|
16516
|
-
this.isRetryable = true;
|
|
16517
|
-
this.debug = options.debug;
|
|
16518
|
-
this.suggestedNextSteps =
|
|
16519
|
-
(_a = options.suggestedNextSteps) !== null && _a !== void 0 ? _a : [
|
|
16520
|
-
'Verify remote browser infrastructure is running and reachable from Agents Server.',
|
|
16521
|
-
'Check firewall and DNS routing for the remote browser host and port.',
|
|
16522
|
-
'Retry later or continue with non-graphical fallback scraping.',
|
|
16523
|
-
];
|
|
16524
|
-
this.cause = options.cause;
|
|
16525
|
-
Object.setPrototypeOf(this, RemoteBrowserUnavailableError.prototype);
|
|
16526
|
-
}
|
|
16527
|
-
}
|
|
16528
|
-
/**
|
|
16529
|
-
* Returns true when an unknown value is one of the remote-browser outage errors.
|
|
16530
|
-
*/
|
|
16531
|
-
function isRemoteBrowserUnavailableError(error) {
|
|
16532
|
-
return error instanceof RemoteBrowserUnavailableError;
|
|
16533
|
-
}
|
|
16534
|
-
/**
|
|
16535
|
-
* Sanitizes a remote websocket endpoint so debug payloads never expose path secrets.
|
|
16536
|
-
*/
|
|
16537
|
-
function sanitizeRemoteBrowserEndpoint(wsEndpoint) {
|
|
16538
|
-
var _a, _b;
|
|
16539
|
-
try {
|
|
16540
|
-
const parsedEndpoint = new URL(wsEndpoint);
|
|
16541
|
-
return {
|
|
16542
|
-
protocol: parsedEndpoint.protocol || null,
|
|
16543
|
-
host: parsedEndpoint.hostname || null,
|
|
16544
|
-
port: parsedEndpoint.port ? Number.parseInt(parsedEndpoint.port, 10) : null,
|
|
16545
|
-
};
|
|
16546
|
-
}
|
|
16547
|
-
catch (_c) {
|
|
16548
|
-
const hostPortMatch = wsEndpoint.trim().match(/^(?:wss?:\/\/)?(?<host>[^:/?#]+)(?::(?<port>\d{1,5}))?/i);
|
|
16549
|
-
const host = ((_a = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _a === void 0 ? void 0 : _a.host) || null;
|
|
16550
|
-
const parsedPort = (_b = hostPortMatch === null || hostPortMatch === void 0 ? void 0 : hostPortMatch.groups) === null || _b === void 0 ? void 0 : _b.port;
|
|
16551
|
-
return {
|
|
16552
|
-
protocol: wsEndpoint.startsWith('wss://') ? 'wss:' : wsEndpoint.startsWith('ws://') ? 'ws:' : null,
|
|
16553
|
-
host,
|
|
16554
|
-
port: parsedPort ? Number.parseInt(parsedPort, 10) : null,
|
|
16555
|
-
};
|
|
16556
|
-
}
|
|
16557
|
-
}
|
|
16558
|
-
/**
|
|
16559
|
-
* Extracts network-like error code from unknown error payload.
|
|
16560
|
-
*/
|
|
16561
|
-
function extractNetworkErrorCode(error) {
|
|
16562
|
-
var _a;
|
|
16563
|
-
if (error && typeof error === 'object') {
|
|
16564
|
-
const maybeCode = error.code;
|
|
16565
|
-
if (typeof maybeCode === 'string' && maybeCode.trim()) {
|
|
16566
|
-
return maybeCode.trim().toUpperCase();
|
|
16567
|
-
}
|
|
16568
|
-
}
|
|
16569
|
-
const message = getErrorMessage(error);
|
|
16570
|
-
const match = message.match(/\b(ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|ECONNRESET|EHOSTUNREACH|ENETUNREACH)\b/i);
|
|
16571
|
-
return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || null;
|
|
16572
|
-
}
|
|
16573
|
-
/**
|
|
16574
|
-
* Classifies whether an unknown error most likely represents remote browser infra outage.
|
|
16575
|
-
*/
|
|
16576
|
-
function isRemoteBrowserInfrastructureError(error) {
|
|
16577
|
-
const networkErrorCode = extractNetworkErrorCode(error);
|
|
16578
|
-
if (networkErrorCode) {
|
|
16579
|
-
return true;
|
|
16580
|
-
}
|
|
16581
|
-
const message = getErrorMessage(error).toLowerCase();
|
|
16582
|
-
const isWebSocketFailure = message.includes('websocket') ||
|
|
16583
|
-
message.includes('<ws error>') ||
|
|
16584
|
-
message.includes('ws connect error') ||
|
|
16585
|
-
message.includes('socket hang up');
|
|
16586
|
-
const hasHandshakeFailure = message.includes('unexpected server response') ||
|
|
16587
|
-
message.includes('handshake') ||
|
|
16588
|
-
message.includes('code=1006') ||
|
|
16589
|
-
message.includes('disconnected');
|
|
16590
|
-
return isWebSocketFailure && hasHandshakeFailure;
|
|
16591
|
-
}
|
|
16592
|
-
/**
|
|
16593
|
-
* Converts unknown thrown values into safe string messages.
|
|
16594
|
-
*/
|
|
16595
|
-
function getErrorMessage(error) {
|
|
16596
|
-
return error instanceof Error ? error.message : String(error);
|
|
16597
|
-
}
|
|
16598
|
-
/**
|
|
16599
|
-
* Converts unknown errors into stack payloads that are safe to render in debug mode.
|
|
16600
|
-
*/
|
|
16601
|
-
function getErrorStack(error) {
|
|
16602
|
-
return error instanceof Error && error.stack ? error.stack : null;
|
|
16603
|
-
}
|
|
16604
|
-
|
|
16605
|
-
/**
|
|
16606
|
-
* Matches unsupported characters in snapshot file suffixes.
|
|
16607
|
-
*/
|
|
16608
|
-
const SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN = /[^a-z0-9-]/g;
|
|
16609
|
-
/**
|
|
16610
|
-
* Creates one filesystem-safe optional filename suffix for a snapshot.
|
|
16611
|
-
*/
|
|
16612
|
-
function createSnapshotFileSuffix(rawSuffix) {
|
|
16613
|
-
if (!rawSuffix) {
|
|
16614
|
-
return '';
|
|
16615
|
-
}
|
|
16616
|
-
const normalized = rawSuffix
|
|
16617
|
-
.trim()
|
|
16618
|
-
.toLowerCase()
|
|
16619
|
-
.replace(/\s+/g, '-')
|
|
16620
|
-
.replace(SNAPSHOT_FILE_SUFFIX_UNSAFE_CHARACTER_PATTERN, '-')
|
|
16621
|
-
.replace(/-+/g, '-')
|
|
16622
|
-
.replace(/^-|-$/g, '');
|
|
16623
|
-
return normalized;
|
|
16624
|
-
}
|
|
16625
|
-
/**
|
|
16626
|
-
* Resolves snapshot filename for one session and optional stage suffix.
|
|
16627
|
-
*/
|
|
16628
|
-
function resolveSnapshotFilename(sessionId, fileSuffix) {
|
|
16629
|
-
const safeSuffix = createSnapshotFileSuffix(fileSuffix);
|
|
16630
|
-
return safeSuffix ? `${sessionId}-${safeSuffix}.png` : `${sessionId}.png`;
|
|
16631
|
-
}
|
|
16632
|
-
/**
|
|
16633
|
-
* Creates one user-facing description for an executed browser action.
|
|
16634
|
-
*/
|
|
16635
|
-
function formatActionSummary(action) {
|
|
16636
|
-
switch (action.type) {
|
|
16637
|
-
case 'navigate':
|
|
16638
|
-
return `Navigate to ${action.url}`;
|
|
16639
|
-
case 'click':
|
|
16640
|
-
return `Click ${action.selector}`;
|
|
16641
|
-
case 'type':
|
|
16642
|
-
return `Type into ${action.selector}`;
|
|
16643
|
-
case 'wait':
|
|
16644
|
-
return `Wait ${action.milliseconds}ms`;
|
|
16645
|
-
case 'scroll':
|
|
16646
|
-
return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
|
|
16647
|
-
}
|
|
16648
|
-
}
|
|
16649
|
-
/**
|
|
16650
|
-
* Screenshot/artifact and page-cleanup helpers for `run_browser`.
|
|
16651
|
-
*
|
|
16652
|
-
* @private function of `run_browser`
|
|
16653
|
-
*/
|
|
16654
|
-
const runBrowserArtifacts = {
|
|
16655
|
-
/**
|
|
16656
|
-
* Captures a screenshot artifact for the current page and returns relative path.
|
|
16657
|
-
*/
|
|
16658
|
-
async captureSnapshot(page, sessionId, fileSuffix) {
|
|
16659
|
-
const snapshotFilename = resolveSnapshotFilename(sessionId, fileSuffix);
|
|
16660
|
-
const snapshotDirectoryPath = resolveRunBrowserArtifactStorageDirectory();
|
|
16661
|
-
const snapshotPath = resolveRunBrowserArtifactFilesystemPath(snapshotFilename);
|
|
16662
|
-
try {
|
|
16663
|
-
await mkdir(snapshotDirectoryPath, { recursive: true });
|
|
16664
|
-
try {
|
|
16665
|
-
await page.screenshot({ path: snapshotPath, fullPage: true });
|
|
16666
|
-
}
|
|
16667
|
-
catch (error) {
|
|
16668
|
-
console.warn('[run_browser] Full-page snapshot failed, retrying viewport-only screenshot', {
|
|
16669
|
-
sessionId,
|
|
16670
|
-
snapshotFilename,
|
|
16671
|
-
error: getErrorMessage(error),
|
|
16672
|
-
});
|
|
16673
|
-
await page.screenshot({ path: snapshotPath, fullPage: false });
|
|
16674
|
-
}
|
|
16675
|
-
return resolveRunBrowserArtifactPublicPath(snapshotFilename);
|
|
16676
|
-
}
|
|
16677
|
-
catch (error) {
|
|
16678
|
-
console.error('[run_browser] Failed to capture snapshot', {
|
|
16679
|
-
sessionId,
|
|
16680
|
-
snapshotFilename,
|
|
16681
|
-
error: getErrorMessage(error),
|
|
16682
|
-
});
|
|
16683
|
-
return null;
|
|
16684
|
-
}
|
|
16685
|
-
},
|
|
16686
|
-
/**
|
|
16687
|
-
* Safely retrieves page title from current browser page.
|
|
16688
|
-
*/
|
|
16689
|
-
async getPageTitle(page) {
|
|
16690
|
-
try {
|
|
16691
|
-
return await page.title();
|
|
16692
|
-
}
|
|
16693
|
-
catch (_a) {
|
|
16694
|
-
return null;
|
|
16695
|
-
}
|
|
16696
|
-
},
|
|
16697
|
-
/**
|
|
16698
|
-
* Closes browser page and logs non-fatal cleanup errors.
|
|
16699
|
-
*/
|
|
16700
|
-
async cleanupPage(page, sessionId) {
|
|
16701
|
-
if (!page) {
|
|
16702
|
-
return;
|
|
16703
|
-
}
|
|
16704
|
-
try {
|
|
16705
|
-
await page.close();
|
|
16706
|
-
}
|
|
16707
|
-
catch (error) {
|
|
16708
|
-
console.error('[run_browser] Failed to cleanup browser page', {
|
|
16709
|
-
sessionId,
|
|
16710
|
-
error: getErrorMessage(error),
|
|
16711
|
-
});
|
|
16712
|
-
}
|
|
16713
|
-
},
|
|
16714
|
-
/**
|
|
16715
|
-
* Captures one screenshot artifact and enriches it with page metadata.
|
|
16716
|
-
*/
|
|
16717
|
-
async captureSnapshotArtifact(options) {
|
|
16718
|
-
const { page, sessionId, label, fileSuffix, actionIndex, action } = options;
|
|
16719
|
-
const path = await this.captureSnapshot(page, sessionId, fileSuffix);
|
|
16720
|
-
if (!path) {
|
|
16721
|
-
return null;
|
|
16722
|
-
}
|
|
16723
|
-
const actionSummary = action ? formatActionSummary(action) : undefined;
|
|
16724
|
-
return {
|
|
16725
|
-
kind: 'screenshot',
|
|
16726
|
-
label,
|
|
16727
|
-
path,
|
|
16728
|
-
capturedAt: new Date().toISOString(),
|
|
16729
|
-
url: page.url(),
|
|
16730
|
-
title: await this.getPageTitle(page),
|
|
16731
|
-
actionIndex,
|
|
16732
|
-
actionSummary,
|
|
16733
|
-
};
|
|
16734
|
-
},
|
|
16735
|
-
};
|
|
16736
|
-
|
|
16737
|
-
/**
|
|
16738
|
-
* Shared constants used by the `run_browser` tool.
|
|
16739
|
-
*
|
|
16740
|
-
* @private internal constants of `run_browser`
|
|
16741
|
-
*/
|
|
16742
|
-
const runBrowserConstants = {
|
|
16743
|
-
sessionPrefix: 'agents-server-run-browser',
|
|
16744
|
-
snapshotDirectory: '.playwright-cli',
|
|
16745
|
-
resultSchema: 'promptbook/run-browser@1',
|
|
16746
|
-
defaultWaitMs: 1000,
|
|
16747
|
-
maxWaitMs: 60000,
|
|
16748
|
-
defaultScrollPixels: 800,
|
|
16749
|
-
defaultNavigationTimeoutMs: 20000,
|
|
16750
|
-
defaultActionTimeoutMs: 15000,
|
|
16751
|
-
fallbackDynamicContentWarning: 'Remote browser is unavailable. Fallback scraping was used and dynamic content may be missing.',
|
|
16752
|
-
validationErrorCode: 'RUN_BROWSER_VALIDATION_ERROR',
|
|
16753
|
-
navigationFailedErrorCode: 'RUN_BROWSER_NAVIGATION_FAILED',
|
|
16754
|
-
actionFailedErrorCode: 'RUN_BROWSER_ACTION_FAILED',
|
|
16755
|
-
cancelledErrorCode: 'RUN_BROWSER_CANCELLED',
|
|
16756
|
-
unknownErrorCode: 'RUN_BROWSER_UNKNOWN_ERROR',
|
|
16757
|
-
};
|
|
16758
|
-
|
|
16759
|
-
const config = ConfigChecker.from({
|
|
16760
|
-
...process.env,
|
|
16761
|
-
// Note: To expose env variables to the browser, using this seemingly strange syntax:
|
|
16762
|
-
// @see https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser
|
|
16763
|
-
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
|
|
16764
|
-
NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV,
|
|
16765
|
-
NEXT_PUBLIC_VERCEL_TARGET_ENV: process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV,
|
|
16766
|
-
NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
|
|
16767
|
-
NEXT_PUBLIC_VERCEL_BRANCH_URL: process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL,
|
|
16768
|
-
NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL: process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL,
|
|
16769
|
-
NEXT_PUBLIC_VERCEL_GIT_PROVIDER: process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
|
|
16770
|
-
NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
|
|
16771
|
-
NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
|
|
16772
|
-
NEXT_PUBLIC_VERCEL_GIT_REPO_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_REPO_ID,
|
|
16773
|
-
NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
|
|
16774
|
-
NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
|
|
16775
|
-
NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
|
|
16776
|
-
NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME,
|
|
16777
|
-
NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN,
|
|
16778
|
-
NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA: process.env.NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA,
|
|
16779
|
-
NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID: process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
|
|
16780
|
-
});
|
|
16781
|
-
/**
|
|
16782
|
-
* Public URL of the deployment, e.g. "https://my-app.vercel.app"
|
|
16783
|
-
*
|
|
16784
|
-
* Note: When a request resolves through the global `_Server` registry,
|
|
16785
|
-
* this URL will be overridden by the matched server domain.
|
|
16786
|
-
*/
|
|
16787
|
-
config.get('NEXT_PUBLIC_SITE_URL').url().value;
|
|
16788
|
-
/**
|
|
16789
|
-
* [âïļ] Vercel environment: "development" | "preview" | "production"
|
|
16790
|
-
*/
|
|
16791
|
-
config.get('NEXT_PUBLIC_VERCEL_ENV').value;
|
|
16792
|
-
/**
|
|
16793
|
-
* [âïļ] Target environment â can be system or custom
|
|
16794
|
-
*/
|
|
16795
|
-
config.get('NEXT_PUBLIC_VERCEL_TARGET_ENV').value;
|
|
16796
|
-
/**
|
|
16797
|
-
* [âïļ] Deployment URL (without https://), e.g. "my-app-abc123.vercel.app"
|
|
16798
|
-
*/
|
|
16799
|
-
config.get('NEXT_PUBLIC_VERCEL_URL').value;
|
|
16800
|
-
/**
|
|
16801
|
-
* [âïļ] Branch URL (without https://), only for branch deployments
|
|
16802
|
-
*/
|
|
16803
|
-
config.get('NEXT_PUBLIC_VERCEL_BRANCH_URL').value;
|
|
16804
|
-
/**
|
|
16805
|
-
* [âïļ] Production domain of the project
|
|
16806
|
-
*/
|
|
16807
|
-
config.get('NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL').value;
|
|
16808
|
-
/**
|
|
16809
|
-
* [âïļ] Git provider (github | gitlab | bitbucket)
|
|
16810
|
-
*/
|
|
16811
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_PROVIDER').value;
|
|
16812
|
-
/**
|
|
16813
|
-
* [âïļ] Repository owner (e.g. "hejny")
|
|
16814
|
-
*/
|
|
16815
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER').value;
|
|
16816
|
-
/**
|
|
16817
|
-
* [âïļ] Repository slug (e.g. "my-project")
|
|
16818
|
-
*/
|
|
16819
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG').value;
|
|
16820
|
-
/**
|
|
16821
|
-
* [âïļ] Repository internal ID
|
|
16822
|
-
*/
|
|
16823
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_REPO_ID').value;
|
|
16824
|
-
/**
|
|
16825
|
-
* [âïļ] Git commit SHA (short or long)
|
|
16826
|
-
*/
|
|
16827
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA').value;
|
|
16828
|
-
/**
|
|
16829
|
-
* [âïļ] Commit message used for this deployment
|
|
16830
|
-
*/
|
|
16831
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE').value;
|
|
16832
|
-
/**
|
|
16833
|
-
* [âïļ] Branch name (ref), e.g. "main"
|
|
16834
|
-
*/
|
|
16835
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF').value;
|
|
16836
|
-
/**
|
|
16837
|
-
* Author name of the commit
|
|
16838
|
-
*/
|
|
16839
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME').value;
|
|
16840
|
-
/**
|
|
16841
|
-
* [âïļ] Author login/username
|
|
16842
|
-
*/
|
|
16843
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_LOGIN').value;
|
|
16844
|
-
/**
|
|
16845
|
-
* [âïļ] Previous deployment commit SHA (if exists)
|
|
16846
|
-
*/
|
|
16847
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_PREVIOUS_SHA').value;
|
|
16848
|
-
/**
|
|
16849
|
-
* [âïļ] Pull Request ID for PR-based deployments
|
|
16850
|
-
*/
|
|
16851
|
-
config.get('NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID').value;
|
|
16852
|
-
/**
|
|
16853
|
-
* Supabase table prefix
|
|
16854
|
-
*
|
|
16855
|
-
* This remains the fallback/default prefix used before `_Server` contains records
|
|
16856
|
-
* or for local development requests.
|
|
16857
|
-
*/
|
|
16858
|
-
config.get('SUPABASE_TABLE_PREFIX').value;
|
|
16859
|
-
/**
|
|
16860
|
-
* WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
|
|
16861
|
-
*
|
|
16862
|
-
* When set, browser automation will connect to this remote server instead of launching a local browser.
|
|
16863
|
-
* This is useful for environments like Vercel where running a full browser locally is not possible.
|
|
16864
|
-
* Leave empty to use local browser mode.
|
|
16865
|
-
*/
|
|
16866
|
-
const rawRemoteBrowserUrl = config.get('REMOTE_BROWSER_URL').value;
|
|
16867
|
-
/**
|
|
16868
|
-
* WebSocket endpoint URL for remote Playwright browser server (e.g., ws://browser-server:3000).
|
|
16869
|
-
*
|
|
16870
|
-
* When set, browser automation will connect to this remote server instead of launching a local browser.
|
|
16871
|
-
* This is useful for environments like Vercel where running a full browser locally is not possible.
|
|
16872
|
-
* Leave empty to use local browser mode.
|
|
16873
|
-
*/
|
|
16874
|
-
const REMOTE_BROWSER_URL = typeof rawRemoteBrowserUrl === 'string' ? rawRemoteBrowserUrl : '';
|
|
16875
|
-
|
|
16876
|
-
/**
|
|
16877
|
-
* Reads a positive integer value from environment variables.
|
|
16878
|
-
*/
|
|
16879
|
-
function resolvePositiveIntFromEnv$1(variableName, defaultValue) {
|
|
16880
|
-
const rawValue = process.env[variableName];
|
|
16881
|
-
if (!rawValue || !rawValue.trim()) {
|
|
16882
|
-
return defaultValue;
|
|
16883
|
-
}
|
|
16884
|
-
const parsed = Number.parseInt(rawValue.trim(), 10);
|
|
16885
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
16886
|
-
return defaultValue;
|
|
16887
|
-
}
|
|
16888
|
-
return parsed;
|
|
16889
|
-
}
|
|
16890
|
-
/**
|
|
16891
|
-
* Runtime helpers for mode/session/timeout handling in `run_browser`.
|
|
16892
|
-
*
|
|
16893
|
-
* @private function of `run_browser`
|
|
16894
|
-
*/
|
|
16895
|
-
const runBrowserRuntime = {
|
|
16896
|
-
/**
|
|
16897
|
-
* Creates a dedicated session id for one tool invocation.
|
|
16898
|
-
*/
|
|
16899
|
-
createRunBrowserSessionId() {
|
|
16900
|
-
return `${runBrowserConstants.sessionPrefix}-${randomUUID()}`;
|
|
16901
|
-
},
|
|
16902
|
-
/**
|
|
16903
|
-
* Determines whether the browser tool is running in local or remote mode.
|
|
16904
|
-
*/
|
|
16905
|
-
resolveExecutionMode() {
|
|
16906
|
-
return REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0 ? 'remote' : 'local';
|
|
16907
|
-
},
|
|
16908
|
-
/**
|
|
16909
|
-
* Converts the execution mode into a human-readable label.
|
|
16910
|
-
*/
|
|
16911
|
-
formatExecutionMode(mode) {
|
|
16912
|
-
return mode === 'remote' ? 'remote-browser' : 'local-browser';
|
|
16913
|
-
},
|
|
16914
|
-
/**
|
|
16915
|
-
* Resolves timeout configuration from env defaults and optional call overrides.
|
|
16916
|
-
*/
|
|
16917
|
-
resolveTimeoutConfiguration(overrides) {
|
|
16918
|
-
const envNavigationTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_NAVIGATION_TIMEOUT_MS', runBrowserConstants.defaultNavigationTimeoutMs);
|
|
16919
|
-
const envActionTimeoutMs = resolvePositiveIntFromEnv$1('RUN_BROWSER_ACTION_TIMEOUT_MS', runBrowserConstants.defaultActionTimeoutMs);
|
|
16920
|
-
const navigationTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.navigationMs) && Number.isFinite(overrides.navigationMs) && overrides.navigationMs > 0
|
|
16921
|
-
? Math.floor(overrides.navigationMs)
|
|
16922
|
-
: envNavigationTimeoutMs;
|
|
16923
|
-
const actionTimeoutMs = (overrides === null || overrides === void 0 ? void 0 : overrides.actionMs) && Number.isFinite(overrides.actionMs) && overrides.actionMs > 0
|
|
16924
|
-
? Math.floor(overrides.actionMs)
|
|
16925
|
-
: envActionTimeoutMs;
|
|
16926
|
-
return {
|
|
16927
|
-
navigationTimeoutMs,
|
|
16928
|
-
actionTimeoutMs,
|
|
16929
|
-
};
|
|
16930
|
-
},
|
|
16931
|
-
};
|
|
16932
|
-
|
|
16933
|
-
/**
|
|
16934
|
-
* Error classification and cancellation helpers used by `run_browser`.
|
|
16935
|
-
*
|
|
16936
|
-
* @private function of `run_browser`
|
|
16937
|
-
*/
|
|
16938
|
-
const runBrowserErrorHandling = {
|
|
16939
|
-
/**
|
|
16940
|
-
* Creates one tagged ParseError used for deterministic input validation failures.
|
|
16941
|
-
*/
|
|
16942
|
-
createRunBrowserValidationError(options) {
|
|
16943
|
-
const error = new ParseError(options.message);
|
|
16944
|
-
error.name = 'RunBrowserValidationError';
|
|
16945
|
-
error.runBrowserCode = runBrowserConstants.validationErrorCode;
|
|
16946
|
-
error.isRetryable = false;
|
|
16947
|
-
error.suggestedNextSteps = [
|
|
16948
|
-
'Fix the action payload to match the run_browser schema.',
|
|
16949
|
-
'Check selectors and required action fields before retrying.',
|
|
16950
|
-
];
|
|
16951
|
-
error.debug = options.debug;
|
|
16952
|
-
return error;
|
|
16953
|
-
},
|
|
16954
|
-
/**
|
|
16955
|
-
* Creates one tagged KnowledgeScrapeError used for navigation failures.
|
|
16956
|
-
*/
|
|
16957
|
-
createRunBrowserNavigationError(options) {
|
|
16958
|
-
const error = new KnowledgeScrapeError(options.message);
|
|
16959
|
-
error.name = 'RunBrowserNavigationError';
|
|
16960
|
-
error.runBrowserCode = runBrowserConstants.navigationFailedErrorCode;
|
|
16961
|
-
error.isRetryable = false;
|
|
16962
|
-
error.suggestedNextSteps = [
|
|
16963
|
-
'Verify the URL is reachable and not blocked.',
|
|
16964
|
-
'Retry with a simpler action sequence.',
|
|
16965
|
-
];
|
|
16966
|
-
error.debug = options.debug;
|
|
16967
|
-
error.cause = options.cause;
|
|
16968
|
-
return error;
|
|
16969
|
-
},
|
|
16970
|
-
/**
|
|
16971
|
-
* Creates one tagged KnowledgeScrapeError used for action failures.
|
|
16972
|
-
*/
|
|
16973
|
-
createRunBrowserActionError(options) {
|
|
16974
|
-
const error = new KnowledgeScrapeError(options.message);
|
|
16975
|
-
error.name = 'RunBrowserActionError';
|
|
16976
|
-
error.runBrowserCode = runBrowserConstants.actionFailedErrorCode;
|
|
16977
|
-
error.isRetryable = false;
|
|
16978
|
-
error.suggestedNextSteps = [
|
|
16979
|
-
'Verify selectors and action values.',
|
|
16980
|
-
'Reduce the action sequence to isolate the failing step.',
|
|
16981
|
-
];
|
|
16982
|
-
error.debug = options.debug;
|
|
16983
|
-
error.cause = options.cause;
|
|
16984
|
-
return error;
|
|
16985
|
-
},
|
|
16986
|
-
/**
|
|
16987
|
-
* Creates one tagged KnowledgeScrapeError used for cancellation.
|
|
16988
|
-
*/
|
|
16989
|
-
createRunBrowserCancelledError(options) {
|
|
16990
|
-
const error = new KnowledgeScrapeError(options.message);
|
|
16991
|
-
error.name = 'RunBrowserCancelledError';
|
|
16992
|
-
error.runBrowserCode = runBrowserConstants.cancelledErrorCode;
|
|
16993
|
-
error.isRetryable = true;
|
|
16994
|
-
error.suggestedNextSteps = [
|
|
16995
|
-
'Retry while request context is still active.',
|
|
16996
|
-
'Increase timeout if operation is expected to run longer.',
|
|
16997
|
-
];
|
|
16998
|
-
error.debug = options.debug;
|
|
16999
|
-
error.cause = options.cause;
|
|
17000
|
-
return error;
|
|
17001
|
-
},
|
|
17002
|
-
/**
|
|
17003
|
-
* Checks whether an unknown error carries run_browser classification tags.
|
|
17004
|
-
*/
|
|
17005
|
-
isTaggedRunBrowserError(error) {
|
|
17006
|
-
if (!error || typeof error !== 'object') {
|
|
17007
|
-
return false;
|
|
17008
|
-
}
|
|
17009
|
-
const candidate = error;
|
|
17010
|
-
return (typeof candidate.runBrowserCode === 'string' &&
|
|
17011
|
-
typeof candidate.isRetryable === 'boolean' &&
|
|
17012
|
-
Array.isArray(candidate.suggestedNextSteps) &&
|
|
17013
|
-
typeof candidate.debug === 'object' &&
|
|
17014
|
-
candidate.debug !== null);
|
|
17015
|
-
},
|
|
17016
|
-
/**
|
|
17017
|
-
* Converts unknown errors into structured tool error payloads.
|
|
17018
|
-
*/
|
|
17019
|
-
classifyRunBrowserToolError(options) {
|
|
17020
|
-
if (isRemoteBrowserUnavailableError(options.error)) {
|
|
17021
|
-
return {
|
|
17022
|
-
code: options.error.code,
|
|
17023
|
-
message: options.error.message,
|
|
17024
|
-
isRetryable: options.error.isRetryable,
|
|
17025
|
-
suggestedNextSteps: options.error.suggestedNextSteps,
|
|
17026
|
-
debug: {
|
|
17027
|
-
...options.error.debug,
|
|
17028
|
-
sessionId: options.sessionId,
|
|
17029
|
-
mode: runBrowserRuntime.formatExecutionMode(options.mode),
|
|
17030
|
-
},
|
|
17031
|
-
};
|
|
17032
|
-
}
|
|
17033
|
-
if (this.isTaggedRunBrowserError(options.error)) {
|
|
17034
|
-
return {
|
|
17035
|
-
code: options.error.runBrowserCode,
|
|
17036
|
-
message: options.error.message,
|
|
17037
|
-
isRetryable: options.error.isRetryable,
|
|
17038
|
-
suggestedNextSteps: options.error.suggestedNextSteps,
|
|
17039
|
-
debug: {
|
|
17040
|
-
...options.error.debug,
|
|
17041
|
-
sessionId: options.sessionId,
|
|
17042
|
-
mode: runBrowserRuntime.formatExecutionMode(options.mode),
|
|
17043
|
-
},
|
|
17044
|
-
};
|
|
17045
|
-
}
|
|
17046
|
-
const remoteBrowserEndpoint = REMOTE_BROWSER_URL && REMOTE_BROWSER_URL.trim().length > 0
|
|
17047
|
-
? sanitizeRemoteBrowserEndpoint(REMOTE_BROWSER_URL.trim())
|
|
17048
|
-
: null;
|
|
17049
|
-
const message = getErrorMessage(options.error);
|
|
17050
|
-
return {
|
|
17051
|
-
code: runBrowserConstants.unknownErrorCode,
|
|
17052
|
-
message,
|
|
17053
|
-
isRetryable: false,
|
|
17054
|
-
suggestedNextSteps: ['Inspect debug details to identify the failing phase.', 'Retry with fewer actions.'],
|
|
17055
|
-
debug: {
|
|
17056
|
-
sessionId: options.sessionId,
|
|
17057
|
-
mode: runBrowserRuntime.formatExecutionMode(options.mode),
|
|
17058
|
-
remoteBrowserEndpoint,
|
|
17059
|
-
message,
|
|
17060
|
-
stack: getErrorStack(options.error),
|
|
17061
|
-
},
|
|
17062
|
-
};
|
|
17063
|
-
},
|
|
17064
|
-
/**
|
|
17065
|
-
* Asserts that the run was not aborted.
|
|
17066
|
-
*/
|
|
17067
|
-
assertNotAborted(signal, sessionId) {
|
|
17068
|
-
if (!(signal === null || signal === void 0 ? void 0 : signal.aborted)) {
|
|
17069
|
-
return;
|
|
17070
|
-
}
|
|
17071
|
-
throw this.createRunBrowserCancelledError({
|
|
17072
|
-
message: 'run_browser execution was cancelled.',
|
|
17073
|
-
debug: { sessionId },
|
|
17074
|
-
});
|
|
17075
|
-
},
|
|
17076
|
-
/**
|
|
17077
|
-
* Returns true when the tool error represents remote browser unavailability.
|
|
17078
|
-
*/
|
|
17079
|
-
isRemoteBrowserUnavailableCode(code) {
|
|
17080
|
-
return code === REMOTE_BROWSER_UNAVAILABLE_ERROR_CODE;
|
|
17081
|
-
},
|
|
17082
|
-
};
|
|
17083
|
-
|
|
17084
|
-
/**
|
|
17085
|
-
* In-memory observability counters for browser tool execution.
|
|
17086
|
-
*/
|
|
17087
|
-
const RUN_BROWSER_OBSERVABILITY = {
|
|
17088
|
-
totalRuns: 0,
|
|
17089
|
-
fallbackRuns: 0,
|
|
17090
|
-
errorCodeCounts: {},
|
|
17091
|
-
};
|
|
17092
|
-
/**
|
|
17093
|
-
* Observability counters and metric logging for `run_browser`.
|
|
17094
|
-
*
|
|
17095
|
-
* @private function of `run_browser`
|
|
17096
|
-
*/
|
|
17097
|
-
const runBrowserObservability = {
|
|
17098
|
-
/**
|
|
17099
|
-
* Increments total-run counter and returns the updated value.
|
|
17100
|
-
*/
|
|
17101
|
-
incrementTotalRuns() {
|
|
17102
|
-
RUN_BROWSER_OBSERVABILITY.totalRuns++;
|
|
17103
|
-
return RUN_BROWSER_OBSERVABILITY.totalRuns;
|
|
17104
|
-
},
|
|
17105
|
-
/**
|
|
17106
|
-
* Returns current total run count.
|
|
17107
|
-
*/
|
|
17108
|
-
getTotalRuns() {
|
|
17109
|
-
return RUN_BROWSER_OBSERVABILITY.totalRuns;
|
|
17110
|
-
},
|
|
17111
|
-
/**
|
|
17112
|
-
* Increments fallback counter and returns updated metrics.
|
|
17113
|
-
*/
|
|
17114
|
-
incrementFallbackRunsAndGetMetrics() {
|
|
17115
|
-
RUN_BROWSER_OBSERVABILITY.fallbackRuns++;
|
|
17116
|
-
return {
|
|
17117
|
-
fallbackRuns: RUN_BROWSER_OBSERVABILITY.fallbackRuns,
|
|
17118
|
-
fallbackRate: RUN_BROWSER_OBSERVABILITY.totalRuns === 0
|
|
17119
|
-
? 0
|
|
17120
|
-
: RUN_BROWSER_OBSERVABILITY.fallbackRuns / RUN_BROWSER_OBSERVABILITY.totalRuns,
|
|
17121
|
-
};
|
|
17122
|
-
},
|
|
17123
|
-
/**
|
|
17124
|
-
* Increments one error-code counter and returns the updated value.
|
|
17125
|
-
*/
|
|
17126
|
-
incrementRunBrowserErrorCodeCounter(code) {
|
|
17127
|
-
const currentValue = RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] || 0;
|
|
17128
|
-
const nextValue = currentValue + 1;
|
|
17129
|
-
RUN_BROWSER_OBSERVABILITY.errorCodeCounts[code] = nextValue;
|
|
17130
|
-
return nextValue;
|
|
17131
|
-
},
|
|
17132
|
-
/**
|
|
17133
|
-
* Writes one structured metric line for browser-tool observability.
|
|
17134
|
-
*/
|
|
17135
|
-
logRunBrowserMetric(options) {
|
|
17136
|
-
console.info('[run_browser][metric]', {
|
|
17137
|
-
tool: 'run_browser',
|
|
17138
|
-
mode: options.mode,
|
|
17139
|
-
sessionId: options.sessionId,
|
|
17140
|
-
event: options.event,
|
|
17141
|
-
...(options.payload || {}),
|
|
17142
|
-
});
|
|
17143
|
-
},
|
|
17144
|
-
};
|
|
17145
|
-
|
|
17146
|
-
/**
|
|
17147
|
-
* Computes one compact preview of a fallback scrape payload.
|
|
17148
|
-
*/
|
|
17149
|
-
function createContentPreview(content) {
|
|
17150
|
-
const normalized = content.replace(/\s+/g, ' ').trim();
|
|
17151
|
-
if (normalized.length <= 280) {
|
|
17152
|
-
return normalized;
|
|
17153
|
-
}
|
|
17154
|
-
return `${normalized.slice(0, 277)}...`;
|
|
17155
|
-
}
|
|
17156
|
-
/**
|
|
17157
|
-
* Payload and markdown formatters for `run_browser` outcomes.
|
|
17158
|
-
*
|
|
17159
|
-
* @private function of `run_browser`
|
|
17160
|
-
*/
|
|
17161
|
-
const runBrowserResultFormatting = {
|
|
17162
|
-
/**
|
|
17163
|
-
* Produces one structured payload consumed by chat UI browser replay renderers.
|
|
17164
|
-
*/
|
|
17165
|
-
createResultPayload(options) {
|
|
17166
|
-
return {
|
|
17167
|
-
schema: runBrowserConstants.resultSchema,
|
|
17168
|
-
sessionId: options.sessionId,
|
|
17169
|
-
mode: options.mode,
|
|
17170
|
-
modeUsed: options.modeUsed,
|
|
17171
|
-
initialUrl: options.initialUrl,
|
|
17172
|
-
finalUrl: options.finalUrl,
|
|
17173
|
-
finalTitle: options.finalTitle,
|
|
17174
|
-
executedActions: options.executedActions,
|
|
17175
|
-
artifacts: options.artifacts,
|
|
17176
|
-
warning: options.warning,
|
|
17177
|
-
error: options.error,
|
|
17178
|
-
fallback: options.modeUsed === 'fallback' && options.fallbackContent !== null
|
|
17179
|
-
? {
|
|
17180
|
-
scraper: 'fetch_url_content',
|
|
17181
|
-
contentPreview: createContentPreview(options.fallbackContent),
|
|
17182
|
-
}
|
|
17183
|
-
: null,
|
|
17184
|
-
timing: options.timing,
|
|
17185
|
-
};
|
|
17186
|
-
},
|
|
17187
|
-
/**
|
|
17188
|
-
* Produces a model-friendly markdown summary from browser execution artifacts.
|
|
17189
|
-
*/
|
|
17190
|
-
formatSuccessResult(options) {
|
|
17191
|
-
const { payload, snapshotPath } = options;
|
|
17192
|
-
return spaceTrim$1((block) => {
|
|
17193
|
-
var _a, _b, _c;
|
|
17194
|
-
return `
|
|
17195
|
-
# Browser run completed
|
|
17196
|
-
|
|
17197
|
-
**Session:** ${payload.sessionId}
|
|
17198
|
-
**Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
|
|
17199
|
-
**Mode used:** ${payload.modeUsed}
|
|
17200
|
-
**Initial URL:** ${payload.initialUrl}
|
|
17201
|
-
**Executed actions:** ${payload.executedActions.length}
|
|
17202
|
-
|
|
17203
|
-
## Final page
|
|
17204
|
-
|
|
17205
|
-
- URL: ${payload.finalUrl || 'Unknown'}
|
|
17206
|
-
- Title: ${payload.finalTitle || 'Unknown'}
|
|
17207
|
-
|
|
17208
|
-
## Timings
|
|
17209
|
-
|
|
17210
|
-
- Connect: ${(_a = payload.timing.connectDurationMs) !== null && _a !== void 0 ? _a : 'Unknown'} ms
|
|
17211
|
-
- Initial navigation: ${(_b = payload.timing.initialNavigationDurationMs) !== null && _b !== void 0 ? _b : 'Unknown'} ms
|
|
17212
|
-
- Time to first byte: ${(_c = payload.timing.timeToFirstByteMs) !== null && _c !== void 0 ? _c : 'Unknown'} ms
|
|
17213
|
-
- Total: ${payload.timing.totalDurationMs} ms
|
|
17214
|
-
|
|
17215
|
-
${payload.artifacts.length === 0
|
|
17216
|
-
? ''
|
|
17217
|
-
: `
|
|
17218
|
-
## Visual replay
|
|
17219
|
-
|
|
17220
|
-
${payload.artifacts
|
|
17221
|
-
.map((artifact, index) => {
|
|
17222
|
-
const actionPart = artifact.actionSummary ? ` (${artifact.actionSummary})` : '';
|
|
17223
|
-
return `- ${index + 1}. ${artifact.label}${actionPart}: ${artifact.path}`;
|
|
17224
|
-
})
|
|
17225
|
-
.join('\n')}
|
|
17226
|
-
`}
|
|
17227
|
-
|
|
17228
|
-
${!snapshotPath
|
|
17229
|
-
? ''
|
|
17230
|
-
: `
|
|
17231
|
-
## Final snapshot
|
|
17232
|
-
|
|
17233
|
-
${snapshotPath}
|
|
17234
|
-
`}
|
|
17235
|
-
|
|
17236
|
-
## Playback payload
|
|
17237
|
-
|
|
17238
|
-
\`\`\`json
|
|
17239
|
-
${JSON.stringify(payload, null, 2)}
|
|
17240
|
-
\`\`\`
|
|
17241
|
-
|
|
17242
|
-
${block(payload.executedActions.length === 0
|
|
17243
|
-
? ''
|
|
17244
|
-
: `
|
|
17245
|
-
## Action log
|
|
17246
|
-
|
|
17247
|
-
${payload.executedActions
|
|
17248
|
-
.map((action, index) => `- ${index + 1}. ${JSON.stringify(action)}`)
|
|
17249
|
-
.join('\n')}
|
|
17250
|
-
`)}
|
|
17251
|
-
|
|
17252
|
-
Note: Browser page has been automatically closed to free up resources.
|
|
17253
|
-
`;
|
|
17254
|
-
});
|
|
17255
|
-
},
|
|
17256
|
-
/**
|
|
17257
|
-
* Produces a model-friendly markdown payload when fallback scraping is used.
|
|
17258
|
-
*/
|
|
17259
|
-
formatFallbackResult(options) {
|
|
17260
|
-
const { payload, fallbackContent, requestedActions } = options;
|
|
17261
|
-
return spaceTrim$1(`
|
|
17262
|
-
# Browser run completed with fallback
|
|
17263
|
-
|
|
17264
|
-
**Session:** ${payload.sessionId}
|
|
17265
|
-
**Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
|
|
17266
|
-
**Mode used:** ${payload.modeUsed}
|
|
17267
|
-
**Initial URL:** ${payload.initialUrl}
|
|
17268
|
-
**Requested actions:** ${requestedActions}
|
|
17269
|
-
**Executed actions:** ${payload.executedActions.length}
|
|
17270
|
-
**Warning:** ${payload.warning || runBrowserConstants.fallbackDynamicContentWarning}
|
|
17271
|
-
|
|
17272
|
-
## Extracted content
|
|
17273
|
-
|
|
17274
|
-
${fallbackContent}
|
|
17275
|
-
|
|
17276
|
-
## Playback payload
|
|
17277
|
-
|
|
17278
|
-
\`\`\`json
|
|
17279
|
-
${JSON.stringify(payload, null, 2)}
|
|
17280
|
-
\`\`\`
|
|
17281
|
-
`);
|
|
17282
|
-
},
|
|
17283
|
-
/**
|
|
17284
|
-
* Produces a model-friendly markdown error payload from browser execution failures.
|
|
17285
|
-
*/
|
|
17286
|
-
formatErrorResult(options) {
|
|
17287
|
-
const { payload } = options;
|
|
17288
|
-
const toolError = payload.error;
|
|
17289
|
-
const suggestedNextSteps = (toolError === null || toolError === void 0 ? void 0 : toolError.suggestedNextSteps) || [];
|
|
17290
|
-
return spaceTrim$1(`
|
|
17291
|
-
# Browser run failed
|
|
17292
|
-
|
|
17293
|
-
**Session:** ${payload.sessionId}
|
|
17294
|
-
**Mode requested:** ${runBrowserRuntime.formatExecutionMode(payload.mode)}
|
|
17295
|
-
**Mode used:** ${payload.modeUsed}
|
|
17296
|
-
**Initial URL:** ${payload.initialUrl}
|
|
17297
|
-
**Error code:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.code) || runBrowserConstants.unknownErrorCode}
|
|
17298
|
-
**Error:** ${(toolError === null || toolError === void 0 ? void 0 : toolError.message) || 'Unknown browser tool error'}
|
|
17299
|
-
|
|
17300
|
-
${suggestedNextSteps.length === 0
|
|
17301
|
-
? ''
|
|
17302
|
-
: `
|
|
17303
|
-
## Suggested next steps
|
|
17304
|
-
|
|
17305
|
-
${suggestedNextSteps.map((step) => `- ${step}`).join('\n')}
|
|
17306
|
-
`}
|
|
17307
|
-
|
|
17308
|
-
## Playback payload
|
|
17309
|
-
|
|
17310
|
-
\`\`\`json
|
|
17311
|
-
${JSON.stringify(payload, null, 2)}
|
|
17312
|
-
\`\`\`
|
|
17313
|
-
|
|
17314
|
-
The browser tool could not complete the requested actions.
|
|
17315
|
-
`);
|
|
17316
|
-
},
|
|
17317
|
-
};
|
|
17318
|
-
|
|
17319
|
-
/**
|
|
17320
|
-
* @@@
|
|
17321
|
-
*
|
|
17322
|
-
* @private within the repository
|
|
17323
|
-
*/
|
|
17324
|
-
function locateChrome() {
|
|
17325
|
-
return locateApp({
|
|
17326
|
-
appName: 'Chrome',
|
|
17327
|
-
linuxWhich: 'google-chrome',
|
|
17328
|
-
windowsSuffix: '\\Google\\Chrome\\Application\\chrome.exe',
|
|
17329
|
-
macOsName: 'Google Chrome',
|
|
17330
|
-
});
|
|
17331
|
-
}
|
|
17332
|
-
|
|
17333
|
-
/**
|
|
17334
|
-
* Creates one standard abort error for cancelled retry loops.
|
|
17335
|
-
*
|
|
17336
|
-
* @private utility for Agents Server runtime retries
|
|
17337
|
-
*/
|
|
17338
|
-
function createAbortError$1() {
|
|
17339
|
-
const error = new Error('Operation was aborted.');
|
|
17340
|
-
error.name = 'AbortError';
|
|
17341
|
-
return error;
|
|
17342
|
-
}
|
|
17343
|
-
/**
|
|
17344
|
-
* Throws when the supplied signal is already aborted.
|
|
17345
|
-
*
|
|
17346
|
-
* @private utility for Agents Server runtime retries
|
|
17347
|
-
*/
|
|
17348
|
-
function assertNotAborted(signal) {
|
|
17349
|
-
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
17350
|
-
throw createAbortError$1();
|
|
17351
|
-
}
|
|
17352
|
-
}
|
|
17353
|
-
/**
|
|
17354
|
-
* Waits for a duration while respecting cancellation.
|
|
17355
|
-
*
|
|
17356
|
-
* @private utility for Agents Server runtime retries
|
|
17357
|
-
*/
|
|
17358
|
-
async function sleepWithAbort(delayMs, signal) {
|
|
17359
|
-
if (delayMs <= 0) {
|
|
17360
|
-
assertNotAborted(signal);
|
|
17361
|
-
return;
|
|
17362
|
-
}
|
|
17363
|
-
await new Promise((resolve, reject) => {
|
|
17364
|
-
const timeout = setTimeout(() => {
|
|
17365
|
-
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
17366
|
-
resolve();
|
|
17367
|
-
}, delayMs);
|
|
17368
|
-
const onAbort = () => {
|
|
17369
|
-
clearTimeout(timeout);
|
|
17370
|
-
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
17371
|
-
reject(createAbortError$1());
|
|
17372
|
-
};
|
|
17373
|
-
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
17374
|
-
});
|
|
17375
|
-
}
|
|
17376
|
-
/**
|
|
17377
|
-
* Resolves the retry wait duration for one failed attempt.
|
|
17378
|
-
*
|
|
17379
|
-
* @private utility for Agents Server runtime retries
|
|
17380
|
-
*/
|
|
17381
|
-
function resolveBackoffDelayMs(options) {
|
|
17382
|
-
const exponentialDelay = options.initialDelayMs * Math.pow(options.backoffFactor, Math.max(0, options.attempt - 1));
|
|
17383
|
-
const boundedDelay = Math.min(exponentialDelay, options.maxDelayMs);
|
|
17384
|
-
const jitterDelay = boundedDelay * options.jitterRatio * Math.max(0, options.random());
|
|
17385
|
-
return Math.max(0, Math.round(boundedDelay + jitterDelay));
|
|
17386
|
-
}
|
|
17387
|
-
/**
|
|
17388
|
-
* Retries one async operation with exponential backoff and jitter.
|
|
17389
|
-
*
|
|
17390
|
-
* @private utility for Agents Server runtime retries
|
|
17391
|
-
*/
|
|
17392
|
-
async function retryWithBackoff(operation, options) {
|
|
17393
|
-
var _a, _b, _c;
|
|
17394
|
-
const startedAt = Date.now();
|
|
17395
|
-
const totalAttempts = Math.max(1, options.retries + 1);
|
|
17396
|
-
const random = (_a = options.random) !== null && _a !== void 0 ? _a : Math.random;
|
|
17397
|
-
const sleep = (_b = options.sleep) !== null && _b !== void 0 ? _b : sleepWithAbort;
|
|
17398
|
-
for (let attempt = 1; attempt <= totalAttempts; attempt++) {
|
|
17399
|
-
assertNotAborted(options.signal);
|
|
17400
|
-
try {
|
|
17401
|
-
const value = await operation(attempt);
|
|
17402
|
-
return {
|
|
17403
|
-
value,
|
|
17404
|
-
attempts: attempt,
|
|
17405
|
-
durationMs: Date.now() - startedAt,
|
|
17406
|
-
};
|
|
17407
|
-
}
|
|
17408
|
-
catch (error) {
|
|
17409
|
-
const isLastAttempt = attempt >= totalAttempts;
|
|
17410
|
-
const isRetryable = options.shouldRetry ? options.shouldRetry(error, attempt) : true;
|
|
17411
|
-
if (isLastAttempt || !isRetryable) {
|
|
17412
|
-
throw error;
|
|
17413
|
-
}
|
|
17414
|
-
const delayMs = resolveBackoffDelayMs({
|
|
17415
|
-
attempt,
|
|
17416
|
-
initialDelayMs: options.initialDelayMs,
|
|
17417
|
-
maxDelayMs: options.maxDelayMs,
|
|
17418
|
-
backoffFactor: options.backoffFactor,
|
|
17419
|
-
jitterRatio: options.jitterRatio,
|
|
17420
|
-
random,
|
|
17421
|
-
});
|
|
17422
|
-
(_c = options.onRetry) === null || _c === void 0 ? void 0 : _c.call(options, {
|
|
17423
|
-
attempt,
|
|
17424
|
-
retries: options.retries,
|
|
17425
|
-
delayMs,
|
|
17426
|
-
error,
|
|
17427
|
-
});
|
|
17428
|
-
await sleep(delayMs, options.signal);
|
|
17429
|
-
}
|
|
17430
|
-
}
|
|
17431
|
-
throw new Error('Retry loop exited unexpectedly.');
|
|
17432
|
-
}
|
|
17433
|
-
|
|
17434
|
-
const DEFAULT_BROWSER_USER_DATA_DIR = join(tmpdir(), 'promptbook', 'browser', 'user-data');
|
|
17435
|
-
/**
|
|
17436
|
-
* Default remote browser connect timeout in milliseconds.
|
|
17437
|
-
*/
|
|
17438
|
-
const DEFAULT_REMOTE_CONNECT_TIMEOUT_MS = 10000;
|
|
17439
|
-
/**
|
|
17440
|
-
* Default retry count for remote browser connection establishment.
|
|
17441
|
-
*/
|
|
17442
|
-
const DEFAULT_REMOTE_CONNECT_RETRIES = 2;
|
|
17443
|
-
/**
|
|
17444
|
-
* Default initial retry delay for remote browser connection.
|
|
17445
|
-
*/
|
|
17446
|
-
const DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS = 250;
|
|
17447
|
-
/**
|
|
17448
|
-
* Default maximum retry delay for remote browser connection.
|
|
17449
|
-
*/
|
|
17450
|
-
const DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS = 1000;
|
|
17451
|
-
/**
|
|
17452
|
-
* Default exponential multiplier for remote browser retry delay.
|
|
17453
|
-
*/
|
|
17454
|
-
const DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR = 4;
|
|
17455
|
-
/**
|
|
17456
|
-
* Default retry jitter ratio for remote browser connection.
|
|
17457
|
-
*/
|
|
17458
|
-
const DEFAULT_REMOTE_CONNECT_JITTER_RATIO = 0.2;
|
|
17459
|
-
/**
|
|
17460
|
-
* In-memory metrics counters for remote browser connect attempts.
|
|
17461
|
-
*/
|
|
17462
|
-
const REMOTE_BROWSER_CONNECT_METRICS = {
|
|
17463
|
-
success: 0,
|
|
17464
|
-
failure: 0,
|
|
17465
|
-
};
|
|
17466
|
-
/**
|
|
17467
|
-
* Reads a positive integer from environment variables with a fallback default.
|
|
17468
|
-
*/
|
|
17469
|
-
function resolvePositiveIntFromEnv(variableName, defaultValue) {
|
|
17470
|
-
const rawValue = process.env[variableName];
|
|
17471
|
-
if (!rawValue || !rawValue.trim()) {
|
|
17472
|
-
return defaultValue;
|
|
17473
|
-
}
|
|
17474
|
-
const parsed = Number.parseInt(rawValue.trim(), 10);
|
|
17475
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
17476
|
-
return defaultValue;
|
|
17477
|
-
}
|
|
17478
|
-
return parsed;
|
|
17479
|
-
}
|
|
17480
|
-
/**
|
|
17481
|
-
* Reads a positive number from environment variables with a fallback default.
|
|
17482
|
-
*/
|
|
17483
|
-
function resolvePositiveNumberFromEnv(variableName, defaultValue) {
|
|
17484
|
-
const rawValue = process.env[variableName];
|
|
17485
|
-
if (!rawValue || !rawValue.trim()) {
|
|
17486
|
-
return defaultValue;
|
|
17487
|
-
}
|
|
17488
|
-
const parsed = Number.parseFloat(rawValue.trim());
|
|
17489
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
17490
|
-
return defaultValue;
|
|
17491
|
-
}
|
|
17492
|
-
return parsed;
|
|
17493
|
-
}
|
|
17494
|
-
/**
|
|
17495
|
-
* Reads a non-negative integer from environment variables with a fallback default.
|
|
17496
|
-
*/
|
|
17497
|
-
function resolveNonNegativeIntFromEnv(variableName, defaultValue) {
|
|
17498
|
-
const rawValue = process.env[variableName];
|
|
17499
|
-
if (!rawValue || !rawValue.trim()) {
|
|
17500
|
-
return defaultValue;
|
|
17501
|
-
}
|
|
17502
|
-
const parsed = Number.parseInt(rawValue.trim(), 10);
|
|
17503
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
17504
|
-
return defaultValue;
|
|
17505
|
-
}
|
|
17506
|
-
return parsed;
|
|
17507
|
-
}
|
|
17508
|
-
/**
|
|
17509
|
-
* Reads a non-negative number from environment variables with a fallback default.
|
|
17510
|
-
*/
|
|
17511
|
-
function resolveNonNegativeNumberFromEnv(variableName, defaultValue) {
|
|
17512
|
-
const rawValue = process.env[variableName];
|
|
17513
|
-
if (!rawValue || !rawValue.trim()) {
|
|
17514
|
-
return defaultValue;
|
|
17515
|
-
}
|
|
17516
|
-
const parsed = Number.parseFloat(rawValue.trim());
|
|
17517
|
-
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
17518
|
-
return defaultValue;
|
|
17519
|
-
}
|
|
17520
|
-
return parsed;
|
|
17521
|
-
}
|
|
17522
|
-
/**
|
|
17523
|
-
* Creates one standard abort error.
|
|
17524
|
-
*/
|
|
17525
|
-
function createAbortError() {
|
|
17526
|
-
const error = new Error('Browser connection request was aborted.');
|
|
17527
|
-
error.name = 'AbortError';
|
|
17528
|
-
return error;
|
|
17529
|
-
}
|
|
17530
|
-
/**
|
|
17531
|
-
* Provides browser context instances with support for both local and remote browser connections.
|
|
16326
|
+
* Cached implementation of `run_browser` when it can be resolved.
|
|
17532
16327
|
*
|
|
17533
|
-
*
|
|
17534
|
-
* - Local mode: Launches a persistent Chromium context on the same machine
|
|
17535
|
-
* - Remote mode: Connects to a remote Playwright browser via WebSocket
|
|
17536
|
-
*
|
|
17537
|
-
* The remote mode is useful for environments like Vercel where running a full browser
|
|
17538
|
-
* is not possible due to resource constraints.
|
|
17539
|
-
*
|
|
17540
|
-
* @private internal utility for Agents Server browser tools
|
|
16328
|
+
* @private internal utility for USE BROWSER commitment
|
|
17541
16329
|
*/
|
|
17542
|
-
|
|
17543
|
-
/**
|
|
17544
|
-
* Creates a new BrowserConnectionProvider.
|
|
17545
|
-
*
|
|
17546
|
-
* @param options - Provider options
|
|
17547
|
-
* @param options.isVerbose - Enable verbose logging
|
|
17548
|
-
*/
|
|
17549
|
-
constructor(options = {}) {
|
|
17550
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
17551
|
-
this.browserContext = null;
|
|
17552
|
-
this.connectionMode = null;
|
|
17553
|
-
this.isVerbose = (_a = options.isVerbose) !== null && _a !== void 0 ? _a : false;
|
|
17554
|
-
this.remoteConnectTimeoutMs =
|
|
17555
|
-
(_b = options.remoteConnectTimeoutMs) !== null && _b !== void 0 ? _b : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_TIMEOUT_MS', DEFAULT_REMOTE_CONNECT_TIMEOUT_MS);
|
|
17556
|
-
this.remoteConnectRetries =
|
|
17557
|
-
(_c = options.remoteConnectRetries) !== null && _c !== void 0 ? _c : resolveNonNegativeIntFromEnv('RUN_BROWSER_CONNECT_RETRIES', DEFAULT_REMOTE_CONNECT_RETRIES);
|
|
17558
|
-
this.remoteConnectBackoffInitialMs =
|
|
17559
|
-
(_d = options.remoteConnectBackoffInitialMs) !== null && _d !== void 0 ? _d : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_INITIAL_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_INITIAL_MS);
|
|
17560
|
-
this.remoteConnectBackoffMaxMs =
|
|
17561
|
-
(_e = options.remoteConnectBackoffMaxMs) !== null && _e !== void 0 ? _e : resolvePositiveIntFromEnv('RUN_BROWSER_CONNECT_BACKOFF_MAX_MS', DEFAULT_REMOTE_CONNECT_BACKOFF_MAX_MS);
|
|
17562
|
-
this.remoteConnectBackoffFactor =
|
|
17563
|
-
(_f = options.remoteConnectBackoffFactor) !== null && _f !== void 0 ? _f : resolvePositiveNumberFromEnv('RUN_BROWSER_CONNECT_BACKOFF_FACTOR', DEFAULT_REMOTE_CONNECT_BACKOFF_FACTOR);
|
|
17564
|
-
this.remoteConnectJitterRatio =
|
|
17565
|
-
(_g = options.remoteConnectJitterRatio) !== null && _g !== void 0 ? _g : resolveNonNegativeNumberFromEnv('RUN_BROWSER_CONNECT_JITTER_RATIO', DEFAULT_REMOTE_CONNECT_JITTER_RATIO);
|
|
17566
|
-
this.random = (_h = options.random) !== null && _h !== void 0 ? _h : Math.random;
|
|
17567
|
-
this.sleep = options.sleep;
|
|
17568
|
-
}
|
|
17569
|
-
/**
|
|
17570
|
-
* Gets a browser context, creating a new one if needed.
|
|
17571
|
-
*
|
|
17572
|
-
* This method automatically determines whether to use local or remote browser
|
|
17573
|
-
* based on the REMOTE_BROWSER_URL environment variable.
|
|
17574
|
-
*
|
|
17575
|
-
* @returns Browser context instance
|
|
17576
|
-
*/
|
|
17577
|
-
async getBrowserContext(options = {}) {
|
|
17578
|
-
var _a;
|
|
17579
|
-
if ((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
|
|
17580
|
-
throw createAbortError();
|
|
17581
|
-
}
|
|
17582
|
-
// Check if we have a cached connection that's still valid
|
|
17583
|
-
if (this.browserContext !== null && this.isBrowserContextAlive(this.browserContext)) {
|
|
17584
|
-
return this.browserContext;
|
|
17585
|
-
}
|
|
17586
|
-
// Determine connection mode from configuration
|
|
17587
|
-
const mode = this.resolveConnectionMode();
|
|
17588
|
-
this.connectionMode = mode;
|
|
17589
|
-
if (this.isVerbose) {
|
|
17590
|
-
console.info('[BrowserConnectionProvider] Creating new browser context', {
|
|
17591
|
-
mode: mode.type,
|
|
17592
|
-
wsEndpoint: mode.type === 'remote' ? mode.wsEndpoint : undefined,
|
|
17593
|
-
});
|
|
17594
|
-
}
|
|
17595
|
-
// Create new browser context based on mode
|
|
17596
|
-
if (mode.type === 'local') {
|
|
17597
|
-
this.browserContext = await this.createLocalBrowserContext();
|
|
17598
|
-
}
|
|
17599
|
-
else {
|
|
17600
|
-
this.browserContext = await this.createRemoteBrowserContext(mode.wsEndpoint, options);
|
|
17601
|
-
}
|
|
17602
|
-
return this.browserContext;
|
|
17603
|
-
}
|
|
17604
|
-
/**
|
|
17605
|
-
* Closes all pages in the current browser context.
|
|
17606
|
-
*
|
|
17607
|
-
* This method is useful for cleanup between agent tasks without closing
|
|
17608
|
-
* the entire browser instance.
|
|
17609
|
-
*/
|
|
17610
|
-
async closeAllPages() {
|
|
17611
|
-
if (!this.browserContext) {
|
|
17612
|
-
return;
|
|
17613
|
-
}
|
|
17614
|
-
try {
|
|
17615
|
-
const pages = this.browserContext.pages();
|
|
17616
|
-
if (this.isVerbose) {
|
|
17617
|
-
console.info('[BrowserConnectionProvider] Closing all pages', {
|
|
17618
|
-
pageCount: pages.length,
|
|
17619
|
-
});
|
|
17620
|
-
}
|
|
17621
|
-
await Promise.all(pages.map((page) => page.close().catch((error) => {
|
|
17622
|
-
console.error('[BrowserConnectionProvider] Failed to close page', { error });
|
|
17623
|
-
})));
|
|
17624
|
-
}
|
|
17625
|
-
catch (error) {
|
|
17626
|
-
console.error('[BrowserConnectionProvider] Error closing pages', { error });
|
|
17627
|
-
}
|
|
17628
|
-
}
|
|
17629
|
-
/**
|
|
17630
|
-
* Closes the browser context and disconnects from the browser.
|
|
17631
|
-
*
|
|
17632
|
-
* This should be called when the browser is no longer needed to free up resources.
|
|
17633
|
-
* For local mode, this closes the browser process. For remote mode, it disconnects
|
|
17634
|
-
* from the remote browser but doesn't shut down the remote server.
|
|
17635
|
-
*/
|
|
17636
|
-
async close() {
|
|
17637
|
-
var _a;
|
|
17638
|
-
if (!this.browserContext) {
|
|
17639
|
-
return;
|
|
17640
|
-
}
|
|
17641
|
-
try {
|
|
17642
|
-
if (this.isVerbose) {
|
|
17643
|
-
console.info('[BrowserConnectionProvider] Closing browser context', {
|
|
17644
|
-
mode: (_a = this.connectionMode) === null || _a === void 0 ? void 0 : _a.type,
|
|
17645
|
-
});
|
|
17646
|
-
}
|
|
17647
|
-
await this.browserContext.close();
|
|
17648
|
-
this.browserContext = null;
|
|
17649
|
-
this.connectionMode = null;
|
|
17650
|
-
}
|
|
17651
|
-
catch (error) {
|
|
17652
|
-
console.error('[BrowserConnectionProvider] Error closing browser context', { error });
|
|
17653
|
-
// Reset state even if close fails
|
|
17654
|
-
this.browserContext = null;
|
|
17655
|
-
this.connectionMode = null;
|
|
17656
|
-
}
|
|
17657
|
-
}
|
|
17658
|
-
/**
|
|
17659
|
-
* Checks if a browser context is still alive and connected.
|
|
17660
|
-
*
|
|
17661
|
-
* @param context - Browser context to check
|
|
17662
|
-
* @returns True if the context is connected and usable
|
|
17663
|
-
*/
|
|
17664
|
-
isBrowserContextAlive(context) {
|
|
17665
|
-
try {
|
|
17666
|
-
const browser = context.browser();
|
|
17667
|
-
return browser !== null && browser.isConnected();
|
|
17668
|
-
}
|
|
17669
|
-
catch (_a) {
|
|
17670
|
-
return false;
|
|
17671
|
-
}
|
|
17672
|
-
}
|
|
17673
|
-
/**
|
|
17674
|
-
* Determines whether to use local or remote browser based on configuration.
|
|
17675
|
-
*
|
|
17676
|
-
* @returns Connection mode configuration
|
|
17677
|
-
*/
|
|
17678
|
-
resolveConnectionMode() {
|
|
17679
|
-
const remoteBrowserUrl = REMOTE_BROWSER_URL;
|
|
17680
|
-
if (remoteBrowserUrl && remoteBrowserUrl.trim().length > 0) {
|
|
17681
|
-
return {
|
|
17682
|
-
type: 'remote',
|
|
17683
|
-
wsEndpoint: remoteBrowserUrl.trim(),
|
|
17684
|
-
};
|
|
17685
|
-
}
|
|
17686
|
-
return { type: 'local' };
|
|
17687
|
-
}
|
|
17688
|
-
/**
|
|
17689
|
-
* Creates a local browser context using persistent Chromium.
|
|
17690
|
-
*
|
|
17691
|
-
* @returns Local browser context
|
|
17692
|
-
*/
|
|
17693
|
-
async createLocalBrowserContext() {
|
|
17694
|
-
if (this.isVerbose) {
|
|
17695
|
-
console.info('[BrowserConnectionProvider] Launching local browser context');
|
|
17696
|
-
}
|
|
17697
|
-
const userDataDir = join(DEFAULT_BROWSER_USER_DATA_DIR, 'run-browser');
|
|
17698
|
-
await mkdir(userDataDir, { recursive: true });
|
|
17699
|
-
const launchOptions = {
|
|
17700
|
-
headless: false,
|
|
17701
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
|
|
17702
|
-
};
|
|
17703
|
-
try {
|
|
17704
|
-
const chromePath = await locateChrome();
|
|
17705
|
-
launchOptions.executablePath = chromePath;
|
|
17706
|
-
}
|
|
17707
|
-
catch (error) {
|
|
17708
|
-
if (this.isVerbose) {
|
|
17709
|
-
console.warn('[BrowserConnectionProvider] Could not locate system Chrome; using Playwright bundled Chromium', {
|
|
17710
|
-
error: error instanceof Error ? error.message : String(error),
|
|
17711
|
-
});
|
|
17712
|
-
}
|
|
17713
|
-
}
|
|
17714
|
-
return await chromium.launchPersistentContext(userDataDir, launchOptions);
|
|
17715
|
-
}
|
|
17716
|
-
/**
|
|
17717
|
-
* Creates a remote browser context by connecting to a Playwright server.
|
|
17718
|
-
*
|
|
17719
|
-
* @param wsEndpoint - WebSocket endpoint of the remote Playwright server
|
|
17720
|
-
* @returns Remote browser context
|
|
17721
|
-
*/
|
|
17722
|
-
async createRemoteBrowserContext(wsEndpoint, options) {
|
|
17723
|
-
const endpointDebug = sanitizeRemoteBrowserEndpoint(wsEndpoint);
|
|
17724
|
-
const startedAt = Date.now();
|
|
17725
|
-
if (this.isVerbose) {
|
|
17726
|
-
console.info('[BrowserConnectionProvider] Connecting to remote browser', {
|
|
17727
|
-
endpoint: endpointDebug,
|
|
17728
|
-
connectTimeoutMs: this.remoteConnectTimeoutMs,
|
|
17729
|
-
retries: this.remoteConnectRetries,
|
|
17730
|
-
});
|
|
17731
|
-
}
|
|
17732
|
-
let attempts = 0;
|
|
17733
|
-
try {
|
|
17734
|
-
const connectResult = await retryWithBackoff(async (attempt) => {
|
|
17735
|
-
attempts = attempt;
|
|
17736
|
-
return await chromium.connect(wsEndpoint, {
|
|
17737
|
-
timeout: this.remoteConnectTimeoutMs,
|
|
17738
|
-
});
|
|
17739
|
-
}, {
|
|
17740
|
-
retries: this.remoteConnectRetries,
|
|
17741
|
-
initialDelayMs: this.remoteConnectBackoffInitialMs,
|
|
17742
|
-
maxDelayMs: this.remoteConnectBackoffMaxMs,
|
|
17743
|
-
backoffFactor: this.remoteConnectBackoffFactor,
|
|
17744
|
-
jitterRatio: this.remoteConnectJitterRatio,
|
|
17745
|
-
signal: options.signal,
|
|
17746
|
-
shouldRetry: (error) => isRemoteBrowserInfrastructureError(error),
|
|
17747
|
-
onRetry: ({ attempt, delayMs, error }) => {
|
|
17748
|
-
console.warn('[run_browser][retry]', {
|
|
17749
|
-
tool: 'run_browser',
|
|
17750
|
-
mode: 'remote-browser',
|
|
17751
|
-
sessionId: options.sessionId || null,
|
|
17752
|
-
event: 'remote_browser_connect_retry',
|
|
17753
|
-
attempt,
|
|
17754
|
-
delayMs,
|
|
17755
|
-
endpoint: endpointDebug,
|
|
17756
|
-
errorCode: extractNetworkErrorCode(error),
|
|
17757
|
-
error: getErrorMessage(error),
|
|
17758
|
-
});
|
|
17759
|
-
},
|
|
17760
|
-
random: this.random,
|
|
17761
|
-
sleep: this.sleep,
|
|
17762
|
-
});
|
|
17763
|
-
const browser = connectResult.value;
|
|
17764
|
-
// For remote connections, we need to create a new context
|
|
17765
|
-
// Note: Remote browsers don't support persistent contexts
|
|
17766
|
-
const context = await browser.newContext();
|
|
17767
|
-
REMOTE_BROWSER_CONNECT_METRICS.success++;
|
|
17768
|
-
console.info('[run_browser][metric]', {
|
|
17769
|
-
tool: 'run_browser',
|
|
17770
|
-
mode: 'remote-browser',
|
|
17771
|
-
sessionId: options.sessionId || null,
|
|
17772
|
-
event: 'remote_browser_connect_success',
|
|
17773
|
-
attempts: connectResult.attempts,
|
|
17774
|
-
connectDurationMs: connectResult.durationMs,
|
|
17775
|
-
endpoint: endpointDebug,
|
|
17776
|
-
counter: REMOTE_BROWSER_CONNECT_METRICS.success,
|
|
17777
|
-
});
|
|
17778
|
-
if (this.isVerbose) {
|
|
17779
|
-
console.info('[BrowserConnectionProvider] Successfully connected to remote browser');
|
|
17780
|
-
}
|
|
17781
|
-
return context;
|
|
17782
|
-
}
|
|
17783
|
-
catch (error) {
|
|
17784
|
-
REMOTE_BROWSER_CONNECT_METRICS.failure++;
|
|
17785
|
-
const durationMs = Date.now() - startedAt;
|
|
17786
|
-
const remoteInfraUnavailable = isRemoteBrowserInfrastructureError(error);
|
|
17787
|
-
if (remoteInfraUnavailable) {
|
|
17788
|
-
const remoteBrowserUnavailableError = new RemoteBrowserUnavailableError({
|
|
17789
|
-
message: `Remote browser is unavailable. Could not establish a websocket connection.`,
|
|
17790
|
-
debug: {
|
|
17791
|
-
endpoint: endpointDebug,
|
|
17792
|
-
attempts: Math.max(1, attempts),
|
|
17793
|
-
connectTimeoutMs: this.remoteConnectTimeoutMs,
|
|
17794
|
-
durationMs,
|
|
17795
|
-
networkErrorCode: extractNetworkErrorCode(error),
|
|
17796
|
-
originalMessage: getErrorMessage(error),
|
|
17797
|
-
},
|
|
17798
|
-
cause: error,
|
|
17799
|
-
});
|
|
17800
|
-
console.warn('[run_browser][metric]', {
|
|
17801
|
-
tool: 'run_browser',
|
|
17802
|
-
mode: 'remote-browser',
|
|
17803
|
-
sessionId: options.sessionId || null,
|
|
17804
|
-
event: 'remote_browser_connect_failure',
|
|
17805
|
-
errorCode: remoteBrowserUnavailableError.code,
|
|
17806
|
-
attempts: Math.max(1, attempts),
|
|
17807
|
-
connectDurationMs: durationMs,
|
|
17808
|
-
endpoint: endpointDebug,
|
|
17809
|
-
counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
|
|
17810
|
-
});
|
|
17811
|
-
throw remoteBrowserUnavailableError;
|
|
17812
|
-
}
|
|
17813
|
-
console.error('[run_browser][metric]', {
|
|
17814
|
-
tool: 'run_browser',
|
|
17815
|
-
mode: 'remote-browser',
|
|
17816
|
-
sessionId: options.sessionId || null,
|
|
17817
|
-
event: 'remote_browser_connect_failure',
|
|
17818
|
-
errorCode: 'REMOTE_BROWSER_CONNECT_ERROR',
|
|
17819
|
-
attempts: Math.max(1, attempts),
|
|
17820
|
-
connectDurationMs: durationMs,
|
|
17821
|
-
endpoint: endpointDebug,
|
|
17822
|
-
error: getErrorMessage(error),
|
|
17823
|
-
counter: REMOTE_BROWSER_CONNECT_METRICS.failure,
|
|
17824
|
-
});
|
|
17825
|
-
throw error;
|
|
17826
|
-
}
|
|
17827
|
-
}
|
|
17828
|
-
}
|
|
17829
|
-
|
|
16330
|
+
let cachedRunBrowserTool = null;
|
|
17830
16331
|
/**
|
|
17831
|
-
*
|
|
16332
|
+
* Cached loading error to avoid repeating expensive resolution attempts.
|
|
17832
16333
|
*
|
|
17833
|
-
* @private internal
|
|
16334
|
+
* @private internal utility for USE BROWSER commitment
|
|
17834
16335
|
*/
|
|
17835
|
-
let
|
|
16336
|
+
let cachedRunBrowserToolError = null;
|
|
17836
16337
|
/**
|
|
17837
|
-
*
|
|
17838
|
-
*
|
|
17839
|
-
* This function supports both local and remote browser connections based on environment configuration.
|
|
17840
|
-
* Use REMOTE_BROWSER_URL environment variable to configure a remote Playwright server.
|
|
16338
|
+
* Attempts to load the server-side `run_browser` tool lazily.
|
|
17841
16339
|
*
|
|
17842
|
-
* @
|
|
17843
|
-
* @
|
|
16340
|
+
* @returns Loaded `run_browser` implementation
|
|
16341
|
+
* @private internal utility for USE BROWSER commitment
|
|
17844
16342
|
*/
|
|
17845
|
-
|
|
17846
|
-
if (
|
|
17847
|
-
|
|
16343
|
+
function loadRunBrowserToolForNode() {
|
|
16344
|
+
if (cachedRunBrowserTool !== null) {
|
|
16345
|
+
return cachedRunBrowserTool;
|
|
17848
16346
|
}
|
|
17849
|
-
|
|
17850
|
-
|
|
17851
|
-
/**
|
|
17852
|
-
* TODO: [ð] Unite `xxxForServer` and `xxxForNode` naming
|
|
17853
|
-
*/
|
|
17854
|
-
|
|
17855
|
-
/**
|
|
17856
|
-
* Attempts to compute time-to-first-byte from Playwright response timing.
|
|
17857
|
-
*/
|
|
17858
|
-
function resolveTimeToFirstByteMs(response) {
|
|
17859
|
-
if (!response) {
|
|
17860
|
-
return null;
|
|
16347
|
+
if (cachedRunBrowserToolError !== null) {
|
|
16348
|
+
throw cachedRunBrowserToolError;
|
|
17861
16349
|
}
|
|
17862
16350
|
try {
|
|
17863
|
-
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
|
|
17867
|
-
return Math.round(timing.responseStart - timing.startTime);
|
|
17868
|
-
}
|
|
17869
|
-
}
|
|
17870
|
-
catch (_a) {
|
|
17871
|
-
return null;
|
|
17872
|
-
}
|
|
17873
|
-
return null;
|
|
17874
|
-
}
|
|
17875
|
-
/**
|
|
17876
|
-
* Page open, action normalization and action execution helpers for `run_browser`.
|
|
17877
|
-
*
|
|
17878
|
-
* @private function of `run_browser`
|
|
17879
|
-
*/
|
|
17880
|
-
const runBrowserWorkflow = {
|
|
17881
|
-
/**
|
|
17882
|
-
* Opens a new browser page and navigates to the requested URL.
|
|
17883
|
-
*/
|
|
17884
|
-
async openPageWithUrl(options) {
|
|
17885
|
-
runBrowserErrorHandling.assertNotAborted(options.signal, options.sessionId);
|
|
17886
|
-
const connectStartedAt = Date.now();
|
|
17887
|
-
const browserContext = await $provideBrowserForServer({
|
|
17888
|
-
signal: options.signal,
|
|
17889
|
-
sessionId: options.sessionId,
|
|
17890
|
-
});
|
|
17891
|
-
const connectDurationMs = Date.now() - connectStartedAt;
|
|
17892
|
-
const page = await browserContext.newPage();
|
|
17893
|
-
page.setDefaultNavigationTimeout(options.timeouts.navigationTimeoutMs);
|
|
17894
|
-
page.setDefaultTimeout(options.timeouts.actionTimeoutMs);
|
|
17895
|
-
const navigationStartedAt = Date.now();
|
|
17896
|
-
try {
|
|
17897
|
-
const navigationResponse = await page.goto(options.url, {
|
|
17898
|
-
waitUntil: 'domcontentloaded',
|
|
17899
|
-
timeout: options.timeouts.navigationTimeoutMs,
|
|
17900
|
-
});
|
|
17901
|
-
return {
|
|
17902
|
-
page,
|
|
17903
|
-
connectDurationMs,
|
|
17904
|
-
initialNavigationDurationMs: Date.now() - navigationStartedAt,
|
|
17905
|
-
timeToFirstByteMs: resolveTimeToFirstByteMs(navigationResponse),
|
|
17906
|
-
};
|
|
17907
|
-
}
|
|
17908
|
-
catch (error) {
|
|
17909
|
-
throw runBrowserErrorHandling.createRunBrowserNavigationError({
|
|
17910
|
-
message: `Failed to navigate to \`${options.url}\`.`,
|
|
17911
|
-
debug: {
|
|
17912
|
-
phase: 'initial-navigation',
|
|
17913
|
-
url: options.url,
|
|
17914
|
-
navigationTimeoutMs: options.timeouts.navigationTimeoutMs,
|
|
17915
|
-
},
|
|
17916
|
-
cause: error,
|
|
17917
|
-
});
|
|
17918
|
-
}
|
|
17919
|
-
},
|
|
17920
|
-
/**
|
|
17921
|
-
* Validates and normalizes browser actions received from the model.
|
|
17922
|
-
*/
|
|
17923
|
-
normalizeActions(actions) {
|
|
17924
|
-
if (!actions || actions.length === 0) {
|
|
17925
|
-
return [];
|
|
17926
|
-
}
|
|
17927
|
-
return actions.map((action, index) => this.normalizeAction(action, index));
|
|
17928
|
-
},
|
|
17929
|
-
/**
|
|
17930
|
-
* Validates and normalizes a single action.
|
|
17931
|
-
*/
|
|
17932
|
-
normalizeAction(action, index) {
|
|
17933
|
-
var _a, _b, _c;
|
|
17934
|
-
switch (action.type) {
|
|
17935
|
-
case 'navigate': {
|
|
17936
|
-
const url = String(action.value || '').trim();
|
|
17937
|
-
if (!url) {
|
|
17938
|
-
throw runBrowserErrorHandling.createRunBrowserValidationError({
|
|
17939
|
-
message: spaceTrim$1(`Action ${index + 1}: \`navigate\` requires non-empty \`value\` URL.`),
|
|
17940
|
-
debug: {
|
|
17941
|
-
actionIndex: index + 1,
|
|
17942
|
-
actionType: action.type,
|
|
17943
|
-
},
|
|
17944
|
-
});
|
|
17945
|
-
}
|
|
17946
|
-
return { type: 'navigate', url };
|
|
17947
|
-
}
|
|
17948
|
-
case 'click': {
|
|
17949
|
-
const selector = String(action.selector || '').trim();
|
|
17950
|
-
if (!selector) {
|
|
17951
|
-
throw runBrowserErrorHandling.createRunBrowserValidationError({
|
|
17952
|
-
message: spaceTrim$1(`Action ${index + 1}: \`click\` requires non-empty \`selector\`.`),
|
|
17953
|
-
debug: {
|
|
17954
|
-
actionIndex: index + 1,
|
|
17955
|
-
actionType: action.type,
|
|
17956
|
-
},
|
|
17957
|
-
});
|
|
17958
|
-
}
|
|
17959
|
-
return { type: 'click', selector };
|
|
17960
|
-
}
|
|
17961
|
-
case 'type': {
|
|
17962
|
-
const selector = String(action.selector || '').trim();
|
|
17963
|
-
if (!selector) {
|
|
17964
|
-
throw runBrowserErrorHandling.createRunBrowserValidationError({
|
|
17965
|
-
message: spaceTrim$1(`Action ${index + 1}: \`type\` requires non-empty \`selector\`.`),
|
|
17966
|
-
debug: {
|
|
17967
|
-
actionIndex: index + 1,
|
|
17968
|
-
actionType: action.type,
|
|
17969
|
-
},
|
|
17970
|
-
});
|
|
17971
|
-
}
|
|
17972
|
-
const text = String((_a = action.value) !== null && _a !== void 0 ? _a : '');
|
|
17973
|
-
return { type: 'type', selector, text };
|
|
17974
|
-
}
|
|
17975
|
-
case 'wait': {
|
|
17976
|
-
const requestedValue = Number.parseInt(String((_b = action.value) !== null && _b !== void 0 ? _b : runBrowserConstants.defaultWaitMs), 10);
|
|
17977
|
-
const milliseconds = Number.isFinite(requestedValue)
|
|
17978
|
-
? Math.min(Math.max(requestedValue, 1), runBrowserConstants.maxWaitMs)
|
|
17979
|
-
: runBrowserConstants.defaultWaitMs;
|
|
17980
|
-
return { type: 'wait', milliseconds };
|
|
17981
|
-
}
|
|
17982
|
-
case 'scroll': {
|
|
17983
|
-
const requestedValue = Number.parseInt(String((_c = action.value) !== null && _c !== void 0 ? _c : runBrowserConstants.defaultScrollPixels), 10);
|
|
17984
|
-
const pixels = Number.isFinite(requestedValue) ? requestedValue : runBrowserConstants.defaultScrollPixels;
|
|
17985
|
-
const rawSelector = String(action.selector || '').trim();
|
|
17986
|
-
return { type: 'scroll', selector: rawSelector || null, pixels };
|
|
17987
|
-
}
|
|
17988
|
-
}
|
|
17989
|
-
},
|
|
17990
|
-
/**
|
|
17991
|
-
* Executes one normalized browser action on a Playwright page.
|
|
17992
|
-
*/
|
|
17993
|
-
async executeAction(options) {
|
|
17994
|
-
const { page, action, actionIndex, timeouts, signal } = options;
|
|
17995
|
-
runBrowserErrorHandling.assertNotAborted(signal, `action-${actionIndex}`);
|
|
17996
|
-
try {
|
|
17997
|
-
switch (action.type) {
|
|
17998
|
-
case 'navigate':
|
|
17999
|
-
await page.goto(action.url, {
|
|
18000
|
-
waitUntil: 'domcontentloaded',
|
|
18001
|
-
timeout: timeouts.navigationTimeoutMs,
|
|
18002
|
-
});
|
|
18003
|
-
return;
|
|
18004
|
-
case 'click':
|
|
18005
|
-
await page.locator(action.selector).first().click({ timeout: timeouts.actionTimeoutMs });
|
|
18006
|
-
return;
|
|
18007
|
-
case 'type':
|
|
18008
|
-
await page.locator(action.selector).first().fill(action.text, { timeout: timeouts.actionTimeoutMs });
|
|
18009
|
-
return;
|
|
18010
|
-
case 'wait':
|
|
18011
|
-
if (action.milliseconds > timeouts.actionTimeoutMs) {
|
|
18012
|
-
throw runBrowserErrorHandling.createRunBrowserActionError({
|
|
18013
|
-
message: `Action ${actionIndex}: \`wait\` exceeds action timeout (${timeouts.actionTimeoutMs}ms).`,
|
|
18014
|
-
debug: {
|
|
18015
|
-
actionIndex,
|
|
18016
|
-
action,
|
|
18017
|
-
actionTimeoutMs: timeouts.actionTimeoutMs,
|
|
18018
|
-
},
|
|
18019
|
-
});
|
|
18020
|
-
}
|
|
18021
|
-
await page.waitForTimeout(action.milliseconds);
|
|
18022
|
-
return;
|
|
18023
|
-
case 'scroll':
|
|
18024
|
-
if (action.selector) {
|
|
18025
|
-
await page
|
|
18026
|
-
.locator(action.selector)
|
|
18027
|
-
.first()
|
|
18028
|
-
.scrollIntoViewIfNeeded({ timeout: timeouts.actionTimeoutMs });
|
|
18029
|
-
}
|
|
18030
|
-
await page.mouse.wheel(0, action.pixels);
|
|
18031
|
-
return;
|
|
18032
|
-
}
|
|
18033
|
-
}
|
|
18034
|
-
catch (error) {
|
|
18035
|
-
if (runBrowserErrorHandling.isTaggedRunBrowserError(error)) {
|
|
18036
|
-
throw error;
|
|
18037
|
-
}
|
|
18038
|
-
if (action.type === 'navigate') {
|
|
18039
|
-
throw runBrowserErrorHandling.createRunBrowserNavigationError({
|
|
18040
|
-
message: `Action ${actionIndex}: failed to navigate to \`${action.url}\`.`,
|
|
18041
|
-
debug: {
|
|
18042
|
-
actionIndex,
|
|
18043
|
-
action,
|
|
18044
|
-
navigationTimeoutMs: timeouts.navigationTimeoutMs,
|
|
18045
|
-
},
|
|
18046
|
-
cause: error,
|
|
18047
|
-
});
|
|
18048
|
-
}
|
|
18049
|
-
throw runBrowserErrorHandling.createRunBrowserActionError({
|
|
18050
|
-
message: `Action ${actionIndex}: failed to execute \`${action.type}\`.`,
|
|
18051
|
-
debug: {
|
|
18052
|
-
actionIndex,
|
|
18053
|
-
action,
|
|
18054
|
-
actionTimeoutMs: timeouts.actionTimeoutMs,
|
|
18055
|
-
},
|
|
18056
|
-
cause: error,
|
|
18057
|
-
});
|
|
18058
|
-
}
|
|
18059
|
-
},
|
|
18060
|
-
};
|
|
18061
|
-
|
|
18062
|
-
/**
|
|
18063
|
-
* Summarizes one normalized browser action in user-facing language.
|
|
18064
|
-
*/
|
|
18065
|
-
function formatRunBrowserActionSummary(action) {
|
|
18066
|
-
switch (action.type) {
|
|
18067
|
-
case 'navigate':
|
|
18068
|
-
return `Navigate to ${action.url}`;
|
|
18069
|
-
case 'click':
|
|
18070
|
-
return `Click ${action.selector}`;
|
|
18071
|
-
case 'type':
|
|
18072
|
-
return `Type into ${action.selector}`;
|
|
18073
|
-
case 'wait':
|
|
18074
|
-
return `Wait ${action.milliseconds}ms`;
|
|
18075
|
-
case 'scroll':
|
|
18076
|
-
return action.selector ? `Scroll ${action.pixels}px in ${action.selector}` : `Scroll ${action.pixels}px on page`;
|
|
18077
|
-
}
|
|
18078
|
-
}
|
|
18079
|
-
/**
|
|
18080
|
-
* Emits one incremental browser-tool update when a hidden chat-progress listener is attached.
|
|
18081
|
-
*/
|
|
18082
|
-
function emitRunBrowserProgress(args, update) {
|
|
18083
|
-
emitToolCallProgressFromToolArgs(args, update);
|
|
18084
|
-
}
|
|
18085
|
-
/**
|
|
18086
|
-
* Returns the current timestamp in the branded ISO-8601 format used by tool-call logs.
|
|
18087
|
-
*/
|
|
18088
|
-
function createRunBrowserLogTimestamp() {
|
|
18089
|
-
return new Date().toISOString();
|
|
18090
|
-
}
|
|
18091
|
-
/**
|
|
18092
|
-
* Executes non-graphical fallback scraping.
|
|
18093
|
-
*/
|
|
18094
|
-
async function runFallbackScrape(url) {
|
|
18095
|
-
return await fetchUrlContent(url);
|
|
18096
|
-
}
|
|
18097
|
-
/**
|
|
18098
|
-
* Runs interactive browser automation through Playwright.
|
|
18099
|
-
*
|
|
18100
|
-
* @param args Tool arguments provided by the model.
|
|
18101
|
-
* @param internalOptions Optional runtime options for cancellation.
|
|
18102
|
-
* @returns Markdown summary with structured playback payload.
|
|
18103
|
-
*/
|
|
18104
|
-
async function run_browser(args, internalOptions = {}) {
|
|
18105
|
-
runBrowserObservability.incrementTotalRuns();
|
|
18106
|
-
const startedAt = Date.now();
|
|
18107
|
-
const sessionId = runBrowserRuntime.createRunBrowserSessionId();
|
|
18108
|
-
const initialUrl = String(args.url || '').trim();
|
|
18109
|
-
const mode = runBrowserRuntime.resolveExecutionMode();
|
|
18110
|
-
const timeoutConfiguration = runBrowserRuntime.resolveTimeoutConfiguration(args.timeouts);
|
|
18111
|
-
let page = null;
|
|
18112
|
-
let connectDurationMs = null;
|
|
18113
|
-
let initialNavigationDurationMs = null;
|
|
18114
|
-
let timeToFirstByteMs = null;
|
|
18115
|
-
try {
|
|
18116
|
-
if (!initialUrl) {
|
|
18117
|
-
throw runBrowserErrorHandling.createRunBrowserValidationError({
|
|
18118
|
-
message: 'Missing required `url` argument.',
|
|
18119
|
-
debug: {
|
|
18120
|
-
field: 'url',
|
|
18121
|
-
},
|
|
18122
|
-
});
|
|
18123
|
-
}
|
|
18124
|
-
const normalizedActions = runBrowserWorkflow.normalizeActions(args.actions);
|
|
18125
|
-
runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
|
|
18126
|
-
const openedPage = await runBrowserWorkflow.openPageWithUrl({
|
|
18127
|
-
url: initialUrl,
|
|
18128
|
-
sessionId,
|
|
18129
|
-
timeouts: timeoutConfiguration,
|
|
18130
|
-
signal: internalOptions.signal,
|
|
18131
|
-
});
|
|
18132
|
-
page = openedPage.page;
|
|
18133
|
-
connectDurationMs = openedPage.connectDurationMs;
|
|
18134
|
-
initialNavigationDurationMs = openedPage.initialNavigationDurationMs;
|
|
18135
|
-
timeToFirstByteMs = openedPage.timeToFirstByteMs;
|
|
18136
|
-
emitRunBrowserProgress(args, {
|
|
18137
|
-
state: 'PARTIAL',
|
|
18138
|
-
log: {
|
|
18139
|
-
createdAt: createRunBrowserLogTimestamp(),
|
|
18140
|
-
kind: 'browser-session',
|
|
18141
|
-
title: 'Browser ready',
|
|
18142
|
-
message: 'Opened the initial page and started the browser session.',
|
|
18143
|
-
payload: {
|
|
18144
|
-
sessionId,
|
|
18145
|
-
initialUrl,
|
|
18146
|
-
connectDurationMs,
|
|
18147
|
-
initialNavigationDurationMs,
|
|
18148
|
-
timeToFirstByteMs,
|
|
18149
|
-
},
|
|
18150
|
-
},
|
|
18151
|
-
});
|
|
18152
|
-
const artifacts = [];
|
|
18153
|
-
const initialArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
|
|
18154
|
-
page,
|
|
18155
|
-
sessionId,
|
|
18156
|
-
label: 'Initial page',
|
|
18157
|
-
fileSuffix: 'initial',
|
|
18158
|
-
});
|
|
18159
|
-
if (initialArtifact) {
|
|
18160
|
-
artifacts.push(initialArtifact);
|
|
18161
|
-
}
|
|
18162
|
-
for (const [index, action] of normalizedActions.entries()) {
|
|
18163
|
-
runBrowserErrorHandling.assertNotAborted(internalOptions.signal, sessionId);
|
|
18164
|
-
emitRunBrowserProgress(args, {
|
|
18165
|
-
state: 'PARTIAL',
|
|
18166
|
-
log: {
|
|
18167
|
-
createdAt: createRunBrowserLogTimestamp(),
|
|
18168
|
-
kind: 'browser-action',
|
|
18169
|
-
title: `Action ${index + 1} running`,
|
|
18170
|
-
message: formatRunBrowserActionSummary(action),
|
|
18171
|
-
payload: {
|
|
18172
|
-
actionIndex: index + 1,
|
|
18173
|
-
action,
|
|
18174
|
-
phase: 'running',
|
|
18175
|
-
},
|
|
18176
|
-
},
|
|
18177
|
-
});
|
|
18178
|
-
await runBrowserWorkflow.executeAction({
|
|
18179
|
-
page,
|
|
18180
|
-
action,
|
|
18181
|
-
actionIndex: index + 1,
|
|
18182
|
-
timeouts: timeoutConfiguration,
|
|
18183
|
-
signal: internalOptions.signal,
|
|
18184
|
-
});
|
|
18185
|
-
emitRunBrowserProgress(args, {
|
|
18186
|
-
state: 'PARTIAL',
|
|
18187
|
-
log: {
|
|
18188
|
-
createdAt: createRunBrowserLogTimestamp(),
|
|
18189
|
-
kind: 'browser-action',
|
|
18190
|
-
title: `Action ${index + 1} finished`,
|
|
18191
|
-
message: formatRunBrowserActionSummary(action),
|
|
18192
|
-
payload: {
|
|
18193
|
-
actionIndex: index + 1,
|
|
18194
|
-
action,
|
|
18195
|
-
phase: 'complete',
|
|
18196
|
-
},
|
|
18197
|
-
},
|
|
18198
|
-
});
|
|
18199
|
-
const actionArtifact = await runBrowserArtifacts.captureSnapshotArtifact({
|
|
18200
|
-
page,
|
|
18201
|
-
sessionId,
|
|
18202
|
-
label: `After action ${index + 1}`,
|
|
18203
|
-
fileSuffix: `action-${String(index + 1).padStart(3, '0')}-${action.type}`,
|
|
18204
|
-
actionIndex: index + 1,
|
|
18205
|
-
action,
|
|
18206
|
-
});
|
|
18207
|
-
if (actionArtifact) {
|
|
18208
|
-
artifacts.push(actionArtifact);
|
|
18209
|
-
}
|
|
18210
|
-
}
|
|
18211
|
-
const snapshotPath = await runBrowserArtifacts.captureSnapshot(page, sessionId);
|
|
18212
|
-
const finalUrl = page.url();
|
|
18213
|
-
const finalTitle = await runBrowserArtifacts.getPageTitle(page);
|
|
18214
|
-
if (snapshotPath) {
|
|
18215
|
-
artifacts.push({
|
|
18216
|
-
kind: 'screenshot',
|
|
18217
|
-
label: 'Final page',
|
|
18218
|
-
path: snapshotPath,
|
|
18219
|
-
capturedAt: new Date().toISOString(),
|
|
18220
|
-
url: finalUrl,
|
|
18221
|
-
title: finalTitle,
|
|
18222
|
-
});
|
|
16351
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16352
|
+
const runBrowserModule = require('../../../apps/agents-server/src/tools/run_browser');
|
|
16353
|
+
if (typeof runBrowserModule.run_browser !== 'function') {
|
|
16354
|
+
throw new Error('run_browser value is not a function but ' + typeof runBrowserModule.run_browser);
|
|
18223
16355
|
}
|
|
18224
|
-
|
|
18225
|
-
|
|
18226
|
-
mode,
|
|
18227
|
-
modeUsed: 'remote-browser',
|
|
18228
|
-
initialUrl,
|
|
18229
|
-
finalUrl,
|
|
18230
|
-
finalTitle,
|
|
18231
|
-
executedActions: normalizedActions,
|
|
18232
|
-
artifacts,
|
|
18233
|
-
warning: null,
|
|
18234
|
-
error: null,
|
|
18235
|
-
fallbackContent: null,
|
|
18236
|
-
timing: {
|
|
18237
|
-
connectDurationMs,
|
|
18238
|
-
initialNavigationDurationMs,
|
|
18239
|
-
timeToFirstByteMs,
|
|
18240
|
-
totalDurationMs: Date.now() - startedAt,
|
|
18241
|
-
},
|
|
18242
|
-
});
|
|
18243
|
-
runBrowserObservability.logRunBrowserMetric({
|
|
18244
|
-
event: 'run_browser_success',
|
|
18245
|
-
sessionId,
|
|
18246
|
-
mode: 'remote-browser',
|
|
18247
|
-
payload: {
|
|
18248
|
-
actions: normalizedActions.length,
|
|
18249
|
-
connectDurationMs,
|
|
18250
|
-
initialNavigationDurationMs,
|
|
18251
|
-
timeToFirstByteMs,
|
|
18252
|
-
},
|
|
18253
|
-
});
|
|
18254
|
-
return runBrowserResultFormatting.formatSuccessResult({
|
|
18255
|
-
payload,
|
|
18256
|
-
snapshotPath,
|
|
18257
|
-
});
|
|
16356
|
+
cachedRunBrowserTool = runBrowserModule.run_browser;
|
|
16357
|
+
return cachedRunBrowserTool;
|
|
18258
16358
|
}
|
|
18259
16359
|
catch (error) {
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
|
|
18263
|
-
mode,
|
|
18264
|
-
});
|
|
18265
|
-
const errorCodeCount = runBrowserObservability.incrementRunBrowserErrorCodeCounter(toolError.code);
|
|
18266
|
-
if (runBrowserErrorHandling.isRemoteBrowserUnavailableCode(toolError.code) && initialUrl) {
|
|
18267
|
-
const fallbackContent = await runFallbackScrape(initialUrl);
|
|
18268
|
-
const { fallbackRuns, fallbackRate } = runBrowserObservability.incrementFallbackRunsAndGetMetrics();
|
|
18269
|
-
emitRunBrowserProgress(args, {
|
|
18270
|
-
state: 'PARTIAL',
|
|
18271
|
-
log: {
|
|
18272
|
-
createdAt: createRunBrowserLogTimestamp(),
|
|
18273
|
-
kind: 'warning',
|
|
18274
|
-
level: 'warning',
|
|
18275
|
-
title: 'Fallback enabled',
|
|
18276
|
-
message: 'Remote browser was unavailable, so fallback scraping was used instead.',
|
|
18277
|
-
payload: {
|
|
18278
|
-
errorCode: toolError.code,
|
|
18279
|
-
initialUrl,
|
|
18280
|
-
},
|
|
18281
|
-
},
|
|
18282
|
-
});
|
|
18283
|
-
const payload = runBrowserResultFormatting.createResultPayload({
|
|
18284
|
-
sessionId,
|
|
18285
|
-
mode,
|
|
18286
|
-
modeUsed: 'fallback',
|
|
18287
|
-
initialUrl,
|
|
18288
|
-
finalUrl: null,
|
|
18289
|
-
finalTitle: null,
|
|
18290
|
-
executedActions: [],
|
|
18291
|
-
artifacts: [],
|
|
18292
|
-
warning: runBrowserConstants.fallbackDynamicContentWarning,
|
|
18293
|
-
error: toolError,
|
|
18294
|
-
fallbackContent,
|
|
18295
|
-
timing: {
|
|
18296
|
-
connectDurationMs,
|
|
18297
|
-
initialNavigationDurationMs,
|
|
18298
|
-
timeToFirstByteMs,
|
|
18299
|
-
totalDurationMs: Date.now() - startedAt,
|
|
18300
|
-
},
|
|
18301
|
-
});
|
|
18302
|
-
runBrowserObservability.logRunBrowserMetric({
|
|
18303
|
-
event: 'run_browser_fallback_used',
|
|
18304
|
-
sessionId,
|
|
18305
|
-
mode: 'fallback',
|
|
18306
|
-
payload: {
|
|
18307
|
-
errorCode: toolError.code,
|
|
18308
|
-
errorCodeCount,
|
|
18309
|
-
fallbackRuns,
|
|
18310
|
-
totalRuns: runBrowserObservability.getTotalRuns(),
|
|
18311
|
-
fallbackRate,
|
|
18312
|
-
},
|
|
18313
|
-
});
|
|
18314
|
-
return runBrowserResultFormatting.formatFallbackResult({
|
|
18315
|
-
payload,
|
|
18316
|
-
fallbackContent,
|
|
18317
|
-
requestedActions: Array.isArray(args.actions) ? args.actions.length : 0,
|
|
18318
|
-
});
|
|
18319
|
-
}
|
|
18320
|
-
emitRunBrowserProgress(args, {
|
|
18321
|
-
state: 'ERROR',
|
|
18322
|
-
log: {
|
|
18323
|
-
createdAt: createRunBrowserLogTimestamp(),
|
|
18324
|
-
kind: 'error',
|
|
18325
|
-
level: 'error',
|
|
18326
|
-
title: 'Browser run failed',
|
|
18327
|
-
message: toolError.message,
|
|
18328
|
-
payload: {
|
|
18329
|
-
code: toolError.code,
|
|
18330
|
-
debug: toolError.debug,
|
|
18331
|
-
},
|
|
18332
|
-
},
|
|
18333
|
-
});
|
|
18334
|
-
const payload = runBrowserResultFormatting.createResultPayload({
|
|
18335
|
-
sessionId,
|
|
18336
|
-
mode,
|
|
18337
|
-
modeUsed: 'remote-browser',
|
|
18338
|
-
initialUrl,
|
|
18339
|
-
finalUrl: page ? page.url() : null,
|
|
18340
|
-
finalTitle: page ? await runBrowserArtifacts.getPageTitle(page) : null,
|
|
18341
|
-
executedActions: [],
|
|
18342
|
-
artifacts: [],
|
|
18343
|
-
warning: null,
|
|
18344
|
-
error: toolError,
|
|
18345
|
-
fallbackContent: null,
|
|
18346
|
-
timing: {
|
|
18347
|
-
connectDurationMs,
|
|
18348
|
-
initialNavigationDurationMs,
|
|
18349
|
-
timeToFirstByteMs,
|
|
18350
|
-
totalDurationMs: Date.now() - startedAt,
|
|
18351
|
-
},
|
|
18352
|
-
});
|
|
18353
|
-
runBrowserObservability.logRunBrowserMetric({
|
|
18354
|
-
event: 'run_browser_failed',
|
|
18355
|
-
sessionId,
|
|
18356
|
-
mode: 'remote-browser',
|
|
18357
|
-
payload: {
|
|
18358
|
-
errorCode: toolError.code,
|
|
18359
|
-
errorCodeCount,
|
|
18360
|
-
connectDurationMs,
|
|
18361
|
-
initialNavigationDurationMs,
|
|
18362
|
-
timeToFirstByteMs,
|
|
18363
|
-
},
|
|
18364
|
-
});
|
|
18365
|
-
return runBrowserResultFormatting.formatErrorResult({
|
|
18366
|
-
payload,
|
|
18367
|
-
});
|
|
18368
|
-
}
|
|
18369
|
-
finally {
|
|
18370
|
-
await runBrowserArtifacts.cleanupPage(page, sessionId);
|
|
16360
|
+
assertsError(error);
|
|
16361
|
+
cachedRunBrowserToolError = error;
|
|
16362
|
+
throw error;
|
|
18371
16363
|
}
|
|
18372
16364
|
}
|
|
18373
|
-
|
|
18374
16365
|
/**
|
|
18375
16366
|
* Resolves the server-side implementation of the `run_browser` tool for Node.js environments.
|
|
18376
16367
|
*
|
|
18377
|
-
* This uses lazy
|
|
16368
|
+
* This uses fully lazy resolution to keep CLI startup independent from optional browser tooling.
|
|
18378
16369
|
* When the server tool cannot be resolved, the fallback implementation throws a helpful error.
|
|
18379
16370
|
*
|
|
18380
16371
|
* @private internal utility for USE BROWSER commitment
|
|
18381
16372
|
*/
|
|
18382
16373
|
function resolveRunBrowserToolForNode() {
|
|
18383
|
-
|
|
18384
|
-
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
throw new Error('run_browser value is not a function but ' + typeof run_browser);
|
|
16374
|
+
return async (args) => {
|
|
16375
|
+
try {
|
|
16376
|
+
const runBrowserTool = loadRunBrowserToolForNode();
|
|
16377
|
+
return await runBrowserTool(args);
|
|
18388
16378
|
}
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
catch (error) {
|
|
18392
|
-
assertsError(error);
|
|
18393
|
-
return async () => {
|
|
16379
|
+
catch (error) {
|
|
16380
|
+
assertsError(error);
|
|
18394
16381
|
throw new EnvironmentMismatchError(spaceTrim$1((block) => `
|
|
18395
16382
|
\`run_browser\` tool is not available in this environment.
|
|
18396
16383
|
This commitment requires the Agents Server browser runtime with Playwright CLI.
|
|
@@ -18398,8 +16385,8 @@ function resolveRunBrowserToolForNode() {
|
|
|
18398
16385
|
${error.name}:
|
|
18399
16386
|
${block(error.message)}
|
|
18400
16387
|
`));
|
|
18401
|
-
}
|
|
18402
|
-
}
|
|
16388
|
+
}
|
|
16389
|
+
};
|
|
18403
16390
|
}
|
|
18404
16391
|
|
|
18405
16392
|
/**
|
|
@@ -20234,6 +18221,110 @@ const parseMemoryToolArgs = {
|
|
|
20234
18221
|
},
|
|
20235
18222
|
};
|
|
20236
18223
|
|
|
18224
|
+
/**
|
|
18225
|
+
* Prompt parameter key used to pass hidden runtime context to tool execution.
|
|
18226
|
+
*
|
|
18227
|
+
* @private internal runtime wiring for commitment tools
|
|
18228
|
+
*/
|
|
18229
|
+
const TOOL_RUNTIME_CONTEXT_PARAMETER = 'promptbookToolRuntimeContext';
|
|
18230
|
+
/**
|
|
18231
|
+
* Hidden argument key used to pass runtime context into individual tool calls.
|
|
18232
|
+
*
|
|
18233
|
+
* @private internal runtime wiring for commitment tools
|
|
18234
|
+
*/
|
|
18235
|
+
const TOOL_RUNTIME_CONTEXT_ARGUMENT = '__promptbookToolRuntimeContext';
|
|
18236
|
+
/**
|
|
18237
|
+
* Prompt parameter key used to pass a hidden tool-progress listener token into script execution.
|
|
18238
|
+
*
|
|
18239
|
+
* @private internal runtime wiring for commitment tools
|
|
18240
|
+
*/
|
|
18241
|
+
const TOOL_PROGRESS_TOKEN_PARAMETER = 'promptbookToolProgressToken';
|
|
18242
|
+
/**
|
|
18243
|
+
* Hidden argument key used to pass a tool-progress listener token into individual tool calls.
|
|
18244
|
+
*
|
|
18245
|
+
* @private internal runtime wiring for commitment tools
|
|
18246
|
+
*/
|
|
18247
|
+
const TOOL_PROGRESS_TOKEN_ARGUMENT = '__promptbookToolProgressToken';
|
|
18248
|
+
/**
|
|
18249
|
+
* Monotonic counter used for hidden progress-listener tokens.
|
|
18250
|
+
*
|
|
18251
|
+
* @private internal runtime wiring for commitment tools
|
|
18252
|
+
*/
|
|
18253
|
+
let toolCallProgressListenerCounter = 0;
|
|
18254
|
+
/**
|
|
18255
|
+
* Active tool-progress listeners keyed by hidden execution token.
|
|
18256
|
+
*
|
|
18257
|
+
* @private internal runtime wiring for commitment tools
|
|
18258
|
+
*/
|
|
18259
|
+
const toolCallProgressListeners = new Map();
|
|
18260
|
+
/**
|
|
18261
|
+
* Parses unknown runtime context payload into a normalized object.
|
|
18262
|
+
*
|
|
18263
|
+
* @private internal runtime wiring for commitment tools
|
|
18264
|
+
*/
|
|
18265
|
+
function parseToolRuntimeContext(rawValue) {
|
|
18266
|
+
if (!rawValue) {
|
|
18267
|
+
return null;
|
|
18268
|
+
}
|
|
18269
|
+
let parsed = rawValue;
|
|
18270
|
+
if (typeof rawValue === 'string') {
|
|
18271
|
+
try {
|
|
18272
|
+
parsed = JSON.parse(rawValue);
|
|
18273
|
+
}
|
|
18274
|
+
catch (_a) {
|
|
18275
|
+
return null;
|
|
18276
|
+
}
|
|
18277
|
+
}
|
|
18278
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
18279
|
+
return null;
|
|
18280
|
+
}
|
|
18281
|
+
return parsed;
|
|
18282
|
+
}
|
|
18283
|
+
/**
|
|
18284
|
+
* Reads runtime context attached to tool call arguments.
|
|
18285
|
+
*
|
|
18286
|
+
* @private internal runtime wiring for commitment tools
|
|
18287
|
+
*/
|
|
18288
|
+
function readToolRuntimeContextFromToolArgs(args) {
|
|
18289
|
+
return parseToolRuntimeContext(args[TOOL_RUNTIME_CONTEXT_ARGUMENT]);
|
|
18290
|
+
}
|
|
18291
|
+
/**
|
|
18292
|
+
* Serializes runtime context for prompt parameters.
|
|
18293
|
+
*
|
|
18294
|
+
* @private internal runtime wiring for commitment tools
|
|
18295
|
+
*/
|
|
18296
|
+
function serializeToolRuntimeContext(context) {
|
|
18297
|
+
return JSON.stringify(context);
|
|
18298
|
+
}
|
|
18299
|
+
/**
|
|
18300
|
+
* Registers one in-memory listener that receives progress updates emitted by a running tool.
|
|
18301
|
+
*
|
|
18302
|
+
* The returned token is passed into script execution as a hidden argument so tool implementations
|
|
18303
|
+
* can stream progress without exposing extra parameters to the model.
|
|
18304
|
+
*
|
|
18305
|
+
* @param listener - Listener notified about tool progress.
|
|
18306
|
+
* @returns Hidden token used to route progress updates.
|
|
18307
|
+
* @private internal runtime wiring for commitment tools
|
|
18308
|
+
*/
|
|
18309
|
+
function registerToolCallProgressListener(listener) {
|
|
18310
|
+
toolCallProgressListenerCounter += 1;
|
|
18311
|
+
const token = `tool-progress:${Date.now()}:${toolCallProgressListenerCounter}`;
|
|
18312
|
+
toolCallProgressListeners.set(token, listener);
|
|
18313
|
+
return token;
|
|
18314
|
+
}
|
|
18315
|
+
/**
|
|
18316
|
+
* Unregisters one in-memory progress listener.
|
|
18317
|
+
*
|
|
18318
|
+
* @param token - Token previously created by `registerToolCallProgressListener`.
|
|
18319
|
+
* @private internal runtime wiring for commitment tools
|
|
18320
|
+
*/
|
|
18321
|
+
function unregisterToolCallProgressListener(token) {
|
|
18322
|
+
toolCallProgressListeners.delete(token);
|
|
18323
|
+
}
|
|
18324
|
+
/**
|
|
18325
|
+
* Note: [ð] Ignore a discrepancy between file name and entity name
|
|
18326
|
+
*/
|
|
18327
|
+
|
|
20237
18328
|
/**
|
|
20238
18329
|
* Resolves runtime context from hidden tool arguments.
|
|
20239
18330
|
*
|
|
@@ -27942,9 +26033,9 @@ function createTimeoutSystemMessage(extraInstructions) {
|
|
|
27942
26033
|
return spaceTrim$1((block) => `
|
|
27943
26034
|
Timeout scheduling:
|
|
27944
26035
|
- Use "set_timeout" to wake this same chat thread in the future.
|
|
27945
|
-
-
|
|
26036
|
+
- Use "list_timeouts" to review timeouts across all chats for the same user+agent scope.
|
|
26037
|
+
- "cancel_timeout" accepts a timeout id from any chat in this same user+agent scope.
|
|
27946
26038
|
- 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\`.
|
|
27947
|
-
- Use "cancel_timeout" when a previously scheduled timeout is no longer relevant.
|
|
27948
26039
|
- Do not claim a timer was set or cancelled unless the tool confirms it.
|
|
27949
26040
|
${block(extraInstructions)}
|
|
27950
26041
|
`);
|
|
@@ -28005,13 +26096,6 @@ function parseToolExecutionEnvelope(rawValue) {
|
|
|
28005
26096
|
* @private internal utility of USE TIMEOUT
|
|
28006
26097
|
*/
|
|
28007
26098
|
function createDisabledTimeoutResult(action, message) {
|
|
28008
|
-
if (action === 'set') {
|
|
28009
|
-
return {
|
|
28010
|
-
action,
|
|
28011
|
-
status: 'disabled',
|
|
28012
|
-
message,
|
|
28013
|
-
};
|
|
28014
|
-
}
|
|
28015
26099
|
return {
|
|
28016
26100
|
action,
|
|
28017
26101
|
status: 'disabled',
|
|
@@ -28038,6 +26122,18 @@ function getTimeoutToolRuntimeAdapterOrDisabledResult(action, runtimeContext) {
|
|
|
28038
26122
|
}
|
|
28039
26123
|
}
|
|
28040
26124
|
|
|
26125
|
+
/**
|
|
26126
|
+
* Default number of rows returned by `list_timeouts`.
|
|
26127
|
+
*
|
|
26128
|
+
* @private internal USE TIMEOUT constant
|
|
26129
|
+
*/
|
|
26130
|
+
const DEFAULT_LIST_TIMEOUTS_LIMIT = 20;
|
|
26131
|
+
/**
|
|
26132
|
+
* Hard cap for `list_timeouts` page size.
|
|
26133
|
+
*
|
|
26134
|
+
* @private internal USE TIMEOUT constant
|
|
26135
|
+
*/
|
|
26136
|
+
const MAX_LIST_TIMEOUTS_LIMIT = 100;
|
|
28041
26137
|
/**
|
|
28042
26138
|
* Parses and validates `USE TIMEOUT` tool arguments.
|
|
28043
26139
|
*
|
|
@@ -28072,6 +26168,31 @@ const parseTimeoutToolArgs = {
|
|
|
28072
26168
|
}
|
|
28073
26169
|
return { timeoutId };
|
|
28074
26170
|
},
|
|
26171
|
+
/**
|
|
26172
|
+
* Parses `list_timeouts` input.
|
|
26173
|
+
*/
|
|
26174
|
+
list(args) {
|
|
26175
|
+
if (args.includeFinished !== undefined && typeof args.includeFinished !== 'boolean') {
|
|
26176
|
+
throw new PipelineExecutionError(spaceTrim$1(`
|
|
26177
|
+
Timeout \`includeFinished\` must be a boolean when provided.
|
|
26178
|
+
`));
|
|
26179
|
+
}
|
|
26180
|
+
const parsedLimit = args.limit === undefined ? DEFAULT_LIST_TIMEOUTS_LIMIT : Math.floor(Number(args.limit));
|
|
26181
|
+
if (!Number.isFinite(parsedLimit) || parsedLimit <= 0) {
|
|
26182
|
+
throw new PipelineExecutionError(spaceTrim$1(`
|
|
26183
|
+
Timeout \`limit\` must be a positive number.
|
|
26184
|
+
`));
|
|
26185
|
+
}
|
|
26186
|
+
if (parsedLimit > MAX_LIST_TIMEOUTS_LIMIT) {
|
|
26187
|
+
throw new PipelineExecutionError(spaceTrim$1(`
|
|
26188
|
+
Timeout \`limit\` must be at most \`${MAX_LIST_TIMEOUTS_LIMIT}\`.
|
|
26189
|
+
`));
|
|
26190
|
+
}
|
|
26191
|
+
return {
|
|
26192
|
+
includeFinished: args.includeFinished === true,
|
|
26193
|
+
limit: parsedLimit,
|
|
26194
|
+
};
|
|
26195
|
+
},
|
|
28075
26196
|
};
|
|
28076
26197
|
|
|
28077
26198
|
/**
|
|
@@ -28082,6 +26203,7 @@ const parseTimeoutToolArgs = {
|
|
|
28082
26203
|
const TimeoutToolNames = {
|
|
28083
26204
|
set: 'set_timeout',
|
|
28084
26205
|
cancel: 'cancel_timeout',
|
|
26206
|
+
list: 'list_timeouts',
|
|
28085
26207
|
};
|
|
28086
26208
|
|
|
28087
26209
|
/**
|
|
@@ -28181,6 +26303,35 @@ function createTimeoutToolFunctions() {
|
|
|
28181
26303
|
return JSON.stringify(result);
|
|
28182
26304
|
}
|
|
28183
26305
|
},
|
|
26306
|
+
async [TimeoutToolNames.list](args) {
|
|
26307
|
+
const runtimeContext = resolveTimeoutRuntimeContext(args);
|
|
26308
|
+
const { adapter, disabledResult } = getTimeoutToolRuntimeAdapterOrDisabledResult('list', runtimeContext);
|
|
26309
|
+
if (!adapter || disabledResult) {
|
|
26310
|
+
return JSON.stringify(disabledResult);
|
|
26311
|
+
}
|
|
26312
|
+
try {
|
|
26313
|
+
const parsedArgs = parseTimeoutToolArgs.list(args);
|
|
26314
|
+
const listedTimeouts = await adapter.listTimeouts(parsedArgs, runtimeContext);
|
|
26315
|
+
const result = {
|
|
26316
|
+
action: 'list',
|
|
26317
|
+
status: 'listed',
|
|
26318
|
+
items: listedTimeouts.items,
|
|
26319
|
+
total: listedTimeouts.total,
|
|
26320
|
+
};
|
|
26321
|
+
return createToolExecutionEnvelope({
|
|
26322
|
+
assistantMessage: listedTimeouts.total === 1 ? 'Found 1 timeout.' : `Found ${listedTimeouts.total} timeouts.`,
|
|
26323
|
+
toolResult: result,
|
|
26324
|
+
});
|
|
26325
|
+
}
|
|
26326
|
+
catch (error) {
|
|
26327
|
+
const result = {
|
|
26328
|
+
action: 'list',
|
|
26329
|
+
status: 'error',
|
|
26330
|
+
message: error instanceof Error ? error.message : String(error),
|
|
26331
|
+
};
|
|
26332
|
+
return JSON.stringify(result);
|
|
26333
|
+
}
|
|
26334
|
+
},
|
|
28184
26335
|
};
|
|
28185
26336
|
}
|
|
28186
26337
|
|
|
@@ -28214,26 +26365,45 @@ function createTimeoutTools(existingTools = []) {
|
|
|
28214
26365
|
if (!tools.some((tool) => tool.name === TimeoutToolNames.cancel)) {
|
|
28215
26366
|
tools.push({
|
|
28216
26367
|
name: TimeoutToolNames.cancel,
|
|
28217
|
-
description: 'Cancel one previously scheduled timeout
|
|
26368
|
+
description: 'Cancel one previously scheduled timeout within the same user+agent scope, even if it was set in another chat.',
|
|
28218
26369
|
parameters: {
|
|
28219
26370
|
type: 'object',
|
|
28220
26371
|
properties: {
|
|
28221
26372
|
timeoutId: {
|
|
28222
26373
|
type: 'string',
|
|
28223
|
-
description: 'Identifier returned earlier by `set_timeout`.',
|
|
26374
|
+
description: 'Identifier returned earlier by `set_timeout` or `list_timeouts`.',
|
|
28224
26375
|
},
|
|
28225
26376
|
},
|
|
28226
26377
|
required: ['timeoutId'],
|
|
28227
26378
|
},
|
|
28228
26379
|
});
|
|
28229
26380
|
}
|
|
26381
|
+
if (!tools.some((tool) => tool.name === TimeoutToolNames.list)) {
|
|
26382
|
+
tools.push({
|
|
26383
|
+
name: TimeoutToolNames.list,
|
|
26384
|
+
description: 'List scheduled timeouts across all chats for this same user+agent scope so they can be reviewed or cancelled.',
|
|
26385
|
+
parameters: {
|
|
26386
|
+
type: 'object',
|
|
26387
|
+
properties: {
|
|
26388
|
+
includeFinished: {
|
|
26389
|
+
type: 'boolean',
|
|
26390
|
+
description: 'When true, include completed, failed, and cancelled rows in addition to active timeouts.',
|
|
26391
|
+
},
|
|
26392
|
+
limit: {
|
|
26393
|
+
type: 'number',
|
|
26394
|
+
description: 'Maximum number of rows to return (default 20, max 100).',
|
|
26395
|
+
},
|
|
26396
|
+
},
|
|
26397
|
+
},
|
|
26398
|
+
});
|
|
26399
|
+
}
|
|
28230
26400
|
return tools;
|
|
28231
26401
|
}
|
|
28232
26402
|
|
|
28233
26403
|
/**
|
|
28234
26404
|
* `USE TIMEOUT` commitment definition.
|
|
28235
26405
|
*
|
|
28236
|
-
* The `USE TIMEOUT` commitment enables
|
|
26406
|
+
* The `USE TIMEOUT` commitment enables timeout wake-ups and scoped timeout management.
|
|
28237
26407
|
*
|
|
28238
26408
|
* @private [ðŠ] Maybe export the commitments through some package
|
|
28239
26409
|
*/
|
|
@@ -28248,7 +26418,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
28248
26418
|
* Short one-line description of `USE TIMEOUT`.
|
|
28249
26419
|
*/
|
|
28250
26420
|
get description() {
|
|
28251
|
-
return 'Enable
|
|
26421
|
+
return 'Enable timeout wake-ups plus scoped timeout listing/cancellation across chats.';
|
|
28252
26422
|
}
|
|
28253
26423
|
/**
|
|
28254
26424
|
* Icon for this commitment.
|
|
@@ -28263,14 +26433,15 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
28263
26433
|
return spaceTrim$1(`
|
|
28264
26434
|
# USE TIMEOUT
|
|
28265
26435
|
|
|
28266
|
-
Enables
|
|
26436
|
+
Enables timeout wake-ups and timeout management for the same user+agent scope.
|
|
28267
26437
|
|
|
28268
26438
|
## Key aspects
|
|
28269
26439
|
|
|
28270
26440
|
- The agent uses \`set_timeout\` to schedule a future wake-up in the same chat thread.
|
|
28271
26441
|
- The tool returns immediately while the timeout is stored and executed by the runtime later.
|
|
28272
26442
|
- The wake-up arrives as a new user-like timeout message in the same conversation.
|
|
28273
|
-
- The agent can
|
|
26443
|
+
- The agent can inspect known timeouts via \`list_timeouts\`.
|
|
26444
|
+
- The agent can cancel an existing timeout by \`timeoutId\` via \`cancel_timeout\`, including timeouts created in another chat.
|
|
28274
26445
|
- Commitment content is treated as optional timeout policy instructions.
|
|
28275
26446
|
|
|
28276
26447
|
## Examples
|
|
@@ -28299,6 +26470,7 @@ class UseTimeoutCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
28299
26470
|
return {
|
|
28300
26471
|
[TimeoutToolNames.set]: 'Set timer',
|
|
28301
26472
|
[TimeoutToolNames.cancel]: 'Cancel timer',
|
|
26473
|
+
[TimeoutToolNames.list]: 'List timers',
|
|
28302
26474
|
};
|
|
28303
26475
|
}
|
|
28304
26476
|
/**
|
|
@@ -41438,11 +39610,6 @@ const EMOJIS_OF_SINGLE_PICTOGRAM = new Set(Array.from(EMOJIS).filter((emoji) =>
|
|
|
41438
39610
|
*/
|
|
41439
39611
|
|
|
41440
39612
|
// find-fresh-emoji-tag.ts
|
|
41441
|
-
dotenv.config({ path: '.env' });
|
|
41442
|
-
if (process.cwd() !== join(__dirname, '../..')) {
|
|
41443
|
-
console.error(colors.red(`CWD must be root of the project`));
|
|
41444
|
-
process.exit(1);
|
|
41445
|
-
}
|
|
41446
39613
|
// Note: When run as a standalone script, call the exported function
|
|
41447
39614
|
if (require.main === module) {
|
|
41448
39615
|
findFreshEmojiTag()
|
|
@@ -41455,12 +39622,25 @@ if (require.main === module) {
|
|
|
41455
39622
|
process.exit(0);
|
|
41456
39623
|
});
|
|
41457
39624
|
}
|
|
39625
|
+
/**
|
|
39626
|
+
* Initializes environment and validates repository context for this script.
|
|
39627
|
+
*
|
|
39628
|
+
* @private utility for `findFreshEmojiTag`
|
|
39629
|
+
*/
|
|
39630
|
+
function initializeFindFreshEmojiTagRun() {
|
|
39631
|
+
dotenv.config({ path: '.env' });
|
|
39632
|
+
if (process.cwd() !== join(__dirname, '../..')) {
|
|
39633
|
+
console.error(colors.red(`CWD must be root of the project`));
|
|
39634
|
+
process.exit(1);
|
|
39635
|
+
}
|
|
39636
|
+
}
|
|
41458
39637
|
/**
|
|
41459
39638
|
* Finds fresh emoji tags that are not yet used in the codebase.
|
|
41460
39639
|
*
|
|
41461
39640
|
* @public exported from `@promptbook/cli`
|
|
41462
39641
|
*/
|
|
41463
39642
|
async function findFreshEmojiTag() {
|
|
39643
|
+
initializeFindFreshEmojiTagRun();
|
|
41464
39644
|
console.info(`ðĪŠ Find fresh emoji tag`);
|
|
41465
39645
|
const allFiles = await glob('**/*.{ts,tsx,js,jsx,json,md,txt}', {
|
|
41466
39646
|
ignore: '**/node_modules/**', // <- TODO: [ð°] Ignore also hidden folders like *(`.promptbook`, `.next`, `.git`,...)*
|
|
@@ -41729,11 +39909,6 @@ const PROMPT_SLUG_MAX_LENGTH = 80;
|
|
|
41729
39909
|
* Note: [?] Code in this file should never be published in any package
|
|
41730
39910
|
*/
|
|
41731
39911
|
|
|
41732
|
-
dotenv.config({ path: '.env' });
|
|
41733
|
-
if (process.cwd() !== join(__dirname, '../..')) {
|
|
41734
|
-
console.error(colors.red(`CWD must be root of the project`));
|
|
41735
|
-
process.exit(1);
|
|
41736
|
-
}
|
|
41737
39912
|
if (require.main === module) {
|
|
41738
39913
|
findRefactorCandidates()
|
|
41739
39914
|
.catch((error) => {
|
|
@@ -41746,12 +39921,25 @@ if (require.main === module) {
|
|
|
41746
39921
|
process.exit(0);
|
|
41747
39922
|
});
|
|
41748
39923
|
}
|
|
39924
|
+
/**
|
|
39925
|
+
* Initializes environment and validates repository context for this script.
|
|
39926
|
+
*
|
|
39927
|
+
* @private utility for `findRefactorCandidates`
|
|
39928
|
+
*/
|
|
39929
|
+
function initializeFindRefactorCandidatesRun() {
|
|
39930
|
+
dotenv.config({ path: '.env' });
|
|
39931
|
+
if (process.cwd() !== join(__dirname, '../..')) {
|
|
39932
|
+
console.error(colors.red(`CWD must be root of the project`));
|
|
39933
|
+
process.exit(1);
|
|
39934
|
+
}
|
|
39935
|
+
}
|
|
41749
39936
|
/**
|
|
41750
39937
|
* Orchestrates scanning for refactor candidates and generating prompts.
|
|
41751
39938
|
*
|
|
41752
39939
|
* @public exported from `@promptbook/cli`
|
|
41753
39940
|
*/
|
|
41754
39941
|
async function findRefactorCandidates() {
|
|
39942
|
+
initializeFindRefactorCandidatesRun();
|
|
41755
39943
|
console.info(colors.cyan('?? Find refactor candidates'));
|
|
41756
39944
|
const rootDir = process.cwd();
|
|
41757
39945
|
const promptsDir = join(rootDir, PROMPTS_DIR_NAME);
|