@promptbook/cli 0.112.0-46 → 0.112.0-48
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/README.md +16 -16
- package/esm/index.es.js +1022 -326
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/waitForPause.d.ts +13 -1
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunOctopusVisual.d.ts +13 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -1
- package/esm/scripts/run-codex-prompts/ui/coderRunUiRefresh.d.ts +8 -2
- package/esm/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +36 -0
- package/esm/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +51 -0
- package/esm/src/avatars/AvatarOrImage.d.ts +49 -0
- package/esm/src/avatars/avatarInteractionUtils.d.ts +81 -0
- package/esm/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
- package/esm/src/avatars/avatarPointerTracking.d.ts +17 -0
- package/esm/src/avatars/avatarRenderingUtils.d.ts +3 -2
- package/esm/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
- package/esm/src/avatars/index.d.ts +2 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +41 -1
- package/esm/src/avatars/visuals/asciiOctopusAvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopus3AvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopusAvatarVisualShared.d.ts +159 -0
- package/esm/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatToolCallModalComponents.d.ts +8 -2
- package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
- package/esm/src/book-components/Chat/hooks/useChatCompleteNotification.d.ts +2 -0
- package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +10 -0
- package/esm/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
- package/esm/src/commitments/USE/USE.d.ts +1 -0
- package/esm/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
- package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
- package/esm/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
- package/esm/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
- package/esm/src/commitments/index.d.ts +2 -1
- package/esm/src/config.d.ts +2 -2
- package/esm/src/llm-providers/agent/RemoteAgent.d.ts +3 -0
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
- package/esm/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +49 -5
- package/esm/src/utils/agents/resolveAgentAvatarImageUrl.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +4 -2
- package/umd/index.umd.js +1020 -324
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/waitForPause.d.ts +13 -1
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunOctopusVisual.d.ts +13 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -1
- package/umd/scripts/run-codex-prompts/ui/coderRunUiRefresh.d.ts +8 -2
- package/umd/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +36 -0
- package/umd/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +51 -0
- package/umd/src/avatars/AvatarOrImage.d.ts +49 -0
- package/umd/src/avatars/avatarInteractionUtils.d.ts +81 -0
- package/umd/src/avatars/avatarInteractionUtils.test.d.ts +1 -0
- package/umd/src/avatars/avatarPointerTracking.d.ts +17 -0
- package/umd/src/avatars/avatarRenderingUtils.d.ts +3 -2
- package/umd/src/avatars/avatarRenderingUtils.test.d.ts +1 -0
- package/umd/src/avatars/index.d.ts +2 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +41 -1
- package/umd/src/avatars/visuals/asciiOctopusAvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopus3AvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopusAvatarVisualShared.d.ts +159 -0
- package/umd/src/avatars/visuals/octopusAvatarVisualShared.test.d.ts +1 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatToolCallModalComponents.d.ts +8 -2
- package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.test.d.ts +2 -0
- package/umd/src/book-components/Chat/hooks/useChatCompleteNotification.d.ts +2 -0
- package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +10 -0
- package/umd/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
- package/umd/src/commitments/USE/USE.d.ts +1 -0
- package/umd/src/commitments/USE/aggregateUseCommitmentSystemMessages.d.ts +1 -1
- package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.d.ts +47 -0
- package/umd/src/commitments/USE_DEEPSEARCH/USE_DEEPSEARCH.test.d.ts +1 -0
- package/umd/src/commitments/_common/createSerpSearchToolFunction.d.ts +12 -0
- package/umd/src/commitments/index.d.ts +2 -1
- package/umd/src/config.d.ts +2 -2
- package/umd/src/llm-providers/agent/RemoteAgent.d.ts +3 -0
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.test.d.ts +1 -0
- package/umd/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +49 -5
- package/umd/src/utils/agents/resolveAgentAvatarImageUrl.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
* @generated
|
|
61
61
|
* @see https://github.com/webgptorg/promptbook
|
|
62
62
|
*/
|
|
63
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
63
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-48';
|
|
64
64
|
/**
|
|
65
65
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
66
66
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1047,7 +1047,7 @@
|
|
|
1047
1047
|
*
|
|
1048
1048
|
* @public exported from `@promptbook/core`
|
|
1049
1049
|
*/
|
|
1050
|
-
const CLAIM = `
|
|
1050
|
+
const CLAIM = `Create persistent AI agents that turn your company's scattered knowledge into action`;
|
|
1051
1051
|
// <- TODO: [🐊] Pick the best claim
|
|
1052
1052
|
/**
|
|
1053
1053
|
* Color of the Promptbook
|
|
@@ -1100,7 +1100,7 @@
|
|
|
1100
1100
|
*
|
|
1101
1101
|
* @public exported from `@promptbook/core`
|
|
1102
1102
|
*/
|
|
1103
|
-
const DEFAULT_BOOK_TITLE =
|
|
1103
|
+
const DEFAULT_BOOK_TITLE = `🐙 Untitled agent`;
|
|
1104
1104
|
/**
|
|
1105
1105
|
* When the title of task is not provided, the default title is used
|
|
1106
1106
|
*
|
|
@@ -2490,37 +2490,68 @@
|
|
|
2490
2490
|
}
|
|
2491
2491
|
// Note: [🟡] Code for coder init environment bootstrapping [ensureCoderEnvFile](src/cli/cli-commands/coder/ensureCoderEnvFile.ts) should never be published outside of `@promptbook/cli`
|
|
2492
2492
|
|
|
2493
|
+
/**
|
|
2494
|
+
* @@@
|
|
2495
|
+
*
|
|
2496
|
+
* @public exported from `@promptbook/utils`
|
|
2497
|
+
*/
|
|
2498
|
+
function escapeRegExp$1(value) {
|
|
2499
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2493
2502
|
/**
|
|
2494
2503
|
* Relative path to `.gitignore` in the initialized project.
|
|
2495
2504
|
*/
|
|
2496
2505
|
const GITIGNORE_FILE_PATH = '.gitignore';
|
|
2497
2506
|
/**
|
|
2498
|
-
*
|
|
2507
|
+
* Promptbook coder temp directory that should stay out of version control.
|
|
2508
|
+
*/
|
|
2509
|
+
const CODER_TEMP_GITIGNORE_RULE = '/.tmp';
|
|
2510
|
+
/**
|
|
2511
|
+
* Promptbook coder cache directory that should stay out of version control.
|
|
2512
|
+
*/
|
|
2513
|
+
const CODER_CACHE_GITIGNORE_RULE = '/.promptbook/ptbk-coder';
|
|
2514
|
+
/**
|
|
2515
|
+
* Standard header used when appending Promptbook coder rules into `.gitignore`.
|
|
2499
2516
|
*/
|
|
2500
|
-
const
|
|
2501
|
-
# Promptbook Coder
|
|
2502
|
-
/.tmp
|
|
2503
|
-
`);
|
|
2517
|
+
const CODER_GITIGNORE_HEADER = '# Promptbook Coder';
|
|
2504
2518
|
/**
|
|
2505
|
-
* Ensures `.gitignore` contains the standalone Promptbook coder cache
|
|
2519
|
+
* Ensures `.gitignore` contains the standalone Promptbook coder temp and cache entries.
|
|
2506
2520
|
*
|
|
2507
2521
|
* @private function of `initializeCoderProjectConfiguration`
|
|
2508
2522
|
*/
|
|
2509
2523
|
async function ensureCoderGitignoreFile(projectPath) {
|
|
2510
2524
|
const gitignorePath = path.join(projectPath, GITIGNORE_FILE_PATH);
|
|
2511
2525
|
const currentGitignoreContent = await readTextFileIfExists(gitignorePath);
|
|
2512
|
-
|
|
2526
|
+
const missingRules = getMissingCoderGitignoreRules(currentGitignoreContent || '');
|
|
2527
|
+
if (currentGitignoreContent !== undefined && missingRules.length === 0) {
|
|
2513
2528
|
return 'unchanged';
|
|
2514
2529
|
}
|
|
2515
|
-
const nextGitignoreContent = appendBlock(currentGitignoreContent || '',
|
|
2530
|
+
const nextGitignoreContent = appendBlock(currentGitignoreContent || '', buildCoderGitignoreBlock(missingRules));
|
|
2516
2531
|
await promises.writeFile(gitignorePath, nextGitignoreContent, 'utf-8');
|
|
2517
2532
|
return currentGitignoreContent === undefined ? 'created' : 'updated';
|
|
2518
2533
|
}
|
|
2519
2534
|
/**
|
|
2520
|
-
*
|
|
2535
|
+
* Returns the Promptbook coder gitignore rules that still need to be added.
|
|
2521
2536
|
*/
|
|
2522
|
-
function
|
|
2523
|
-
|
|
2537
|
+
function getMissingCoderGitignoreRules(gitignoreContent) {
|
|
2538
|
+
const requiredRules = [CODER_TEMP_GITIGNORE_RULE, CODER_CACHE_GITIGNORE_RULE];
|
|
2539
|
+
return requiredRules.filter((rule) => !hasGitignoreRule(gitignoreContent, rule));
|
|
2540
|
+
}
|
|
2541
|
+
/**
|
|
2542
|
+
* Builds the Promptbook coder `.gitignore` block for the missing rules only.
|
|
2543
|
+
*/
|
|
2544
|
+
function buildCoderGitignoreBlock(missingRules) {
|
|
2545
|
+
return [CODER_GITIGNORE_HEADER, ...missingRules].join('\n');
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Detects whether `.gitignore` already covers one exact rule.
|
|
2549
|
+
*/
|
|
2550
|
+
function hasGitignoreRule(gitignoreContent, rule) {
|
|
2551
|
+
const normalizedRulePattern = rule.startsWith('/')
|
|
2552
|
+
? `/?${escapeRegExp$1(rule.slice(1))}`
|
|
2553
|
+
: escapeRegExp$1(rule);
|
|
2554
|
+
return new RegExp(`(^|[\\r\\n])${normalizedRulePattern}(?:[\\r\\n]|$)`, 'u').test(gitignoreContent);
|
|
2524
2555
|
}
|
|
2525
2556
|
// Note: [🟡] Code for coder init gitignore bootstrapping [ensureCoderGitignoreFile](src/cli/cli-commands/coder/ensureCoderGitignoreFile.ts) should never be published outside of `@promptbook/cli`
|
|
2526
2557
|
|
|
@@ -2657,7 +2688,7 @@
|
|
|
2657
2688
|
${typescript.flattenDiagnosticMessageText(parsedFile.error.messageText, '\n')}
|
|
2658
2689
|
`));
|
|
2659
2690
|
}
|
|
2660
|
-
if (!isPlainObject(parsedFile.config)) {
|
|
2691
|
+
if (!isPlainObject$1(parsedFile.config)) {
|
|
2661
2692
|
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
2662
2693
|
File \`${relativeFilePath}\` must contain one top-level JSON object.
|
|
2663
2694
|
`));
|
|
@@ -2671,7 +2702,7 @@
|
|
|
2671
2702
|
if (value === undefined) {
|
|
2672
2703
|
return {};
|
|
2673
2704
|
}
|
|
2674
|
-
if (!isPlainObject(value)) {
|
|
2705
|
+
if (!isPlainObject$1(value)) {
|
|
2675
2706
|
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
2676
2707
|
File \`${relativeFilePath}\` contains invalid \`${fieldPath}\`.
|
|
2677
2708
|
|
|
@@ -2716,7 +2747,7 @@
|
|
|
2716
2747
|
/**
|
|
2717
2748
|
* Checks whether one parsed JSON value is a plain object.
|
|
2718
2749
|
*/
|
|
2719
|
-
function isPlainObject(value) {
|
|
2750
|
+
function isPlainObject$1(value) {
|
|
2720
2751
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2721
2752
|
}
|
|
2722
2753
|
// Note: [🟡] Code for coder init JSON merging [mergeStringRecordJsonFile](src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts) should never be published outside of `@promptbook/cli`
|
|
@@ -6196,15 +6227,6 @@
|
|
|
6196
6227
|
// TODO: [🧠][🈴] Where is the best location for this file
|
|
6197
6228
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6198
6229
|
|
|
6199
|
-
/**
|
|
6200
|
-
* @@@
|
|
6201
|
-
*
|
|
6202
|
-
* @public exported from `@promptbook/utils`
|
|
6203
|
-
*/
|
|
6204
|
-
function escapeRegExp$1(value) {
|
|
6205
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
6206
|
-
}
|
|
6207
|
-
|
|
6208
6230
|
/**
|
|
6209
6231
|
* HTTP header used by Promptbook clients to advertise their release version.
|
|
6210
6232
|
*
|
|
@@ -23199,6 +23221,7 @@
|
|
|
23199
23221
|
* Supported USE types:
|
|
23200
23222
|
* - USE BROWSER: Enables the agent to use a web browser tool
|
|
23201
23223
|
* - USE SEARCH ENGINE (future): Enables search engine access
|
|
23224
|
+
* - USE DEEPSEARCH: Enables deeper research-oriented search access
|
|
23202
23225
|
* - USE FILE SYSTEM (future): Enables file system operations
|
|
23203
23226
|
* - USE MCP (future): Enables MCP server connections
|
|
23204
23227
|
*
|
|
@@ -23221,7 +23244,7 @@
|
|
|
23221
23244
|
* Short one-line description of USE commitments.
|
|
23222
23245
|
*/
|
|
23223
23246
|
get description() {
|
|
23224
|
-
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, etc.).';
|
|
23247
|
+
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, DEEPSEARCH, etc.).';
|
|
23225
23248
|
}
|
|
23226
23249
|
/**
|
|
23227
23250
|
* Icon for this commitment.
|
|
@@ -23242,6 +23265,7 @@
|
|
|
23242
23265
|
|
|
23243
23266
|
- **USE BROWSER** - Enables the agent to use a web browser tool to access and retrieve information from the internet
|
|
23244
23267
|
- **USE SEARCH ENGINE** (future) - Enables search engine access
|
|
23268
|
+
- **USE DEEPSEARCH** - Enables deeper research-oriented search access
|
|
23245
23269
|
- **USE FILE SYSTEM** (future) - Enables file system operations
|
|
23246
23270
|
- **USE MCP** (future) - Enables MCP server connections
|
|
23247
23271
|
|
|
@@ -23296,7 +23320,7 @@
|
|
|
23296
23320
|
* Checks if this is a known USE type
|
|
23297
23321
|
*/
|
|
23298
23322
|
isKnownUseType(useType) {
|
|
23299
|
-
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'FILE SYSTEM', 'MCP'];
|
|
23323
|
+
const knownTypes = ['BROWSER', 'SEARCH ENGINE', 'DEEPSEARCH', 'FILE SYSTEM', 'MCP'];
|
|
23300
23324
|
return knownTypes.includes(useType.toUpperCase());
|
|
23301
23325
|
}
|
|
23302
23326
|
}
|
|
@@ -23309,6 +23333,7 @@
|
|
|
23309
23333
|
*/
|
|
23310
23334
|
const AGGREGATED_USE_COMMITMENT_TYPES = [
|
|
23311
23335
|
'USE BROWSER',
|
|
23336
|
+
'USE DEEPSEARCH',
|
|
23312
23337
|
'USE SEARCH ENGINE',
|
|
23313
23338
|
'USE TIME',
|
|
23314
23339
|
];
|
|
@@ -23388,6 +23413,15 @@
|
|
|
23388
23413
|
- Do not tell the user you cannot search for information, YOU CAN.
|
|
23389
23414
|
${block(formatOptionalInstructionBlock('Search instructions', combinedAdditionalInstructions))}
|
|
23390
23415
|
`);
|
|
23416
|
+
case 'USE DEEPSEARCH':
|
|
23417
|
+
return _spaceTrim.spaceTrim((block) => `
|
|
23418
|
+
Tool:
|
|
23419
|
+
- You have access to DeepSearch via the tool "deep_search".
|
|
23420
|
+
- Use it for broader research tasks that need multi-step investigation, comparison, or synthesis across multiple sources.
|
|
23421
|
+
- Prefer it over quick search when the user asks for a well-grounded brief, report, or deeper investigation.
|
|
23422
|
+
- Do not pretend you cannot research current information when this tool is available.
|
|
23423
|
+
${block(formatOptionalInstructionBlock('DeepSearch instructions', combinedAdditionalInstructions))}
|
|
23424
|
+
`);
|
|
23391
23425
|
}
|
|
23392
23426
|
}
|
|
23393
23427
|
/**
|
|
@@ -24937,6 +24971,207 @@
|
|
|
24937
24971
|
}
|
|
24938
24972
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
24939
24973
|
|
|
24974
|
+
/**
|
|
24975
|
+
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
24976
|
+
*
|
|
24977
|
+
* @private <- TODO: !!!! Export via some package
|
|
24978
|
+
*/
|
|
24979
|
+
class SerpSearchEngine {
|
|
24980
|
+
get title() {
|
|
24981
|
+
return 'SerpApi Search Engine';
|
|
24982
|
+
}
|
|
24983
|
+
get description() {
|
|
24984
|
+
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
24985
|
+
}
|
|
24986
|
+
checkConfiguration() {
|
|
24987
|
+
if (!process.env.SERP_API_KEY) {
|
|
24988
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
24989
|
+
}
|
|
24990
|
+
}
|
|
24991
|
+
async search(query, options = {}) {
|
|
24992
|
+
const apiKey = process.env.SERP_API_KEY;
|
|
24993
|
+
if (!apiKey) {
|
|
24994
|
+
throw new Error('SERP_API_KEY is not configured');
|
|
24995
|
+
}
|
|
24996
|
+
const url = new URL('https://serpapi.com/search');
|
|
24997
|
+
url.searchParams.set('api_key', apiKey);
|
|
24998
|
+
url.searchParams.set('engine', 'google');
|
|
24999
|
+
url.searchParams.set('q', query);
|
|
25000
|
+
for (const [key, value] of Object.entries(options)) {
|
|
25001
|
+
url.searchParams.set(key, String(value));
|
|
25002
|
+
}
|
|
25003
|
+
const response = await fetch(url.toString());
|
|
25004
|
+
if (!response.ok) {
|
|
25005
|
+
const body = await response.text();
|
|
25006
|
+
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
25007
|
+
}
|
|
25008
|
+
const data = (await response.json());
|
|
25009
|
+
return (data.organic_results || []).map((item) => ({
|
|
25010
|
+
title: item.title,
|
|
25011
|
+
url: item.link,
|
|
25012
|
+
snippet: item.snippet || '',
|
|
25013
|
+
}));
|
|
25014
|
+
}
|
|
25015
|
+
}
|
|
25016
|
+
|
|
25017
|
+
/**
|
|
25018
|
+
* Creates one SERP-backed tool function used as a local fallback for search-like commitments.
|
|
25019
|
+
*
|
|
25020
|
+
* @param toolName - Technical tool name used for validation messages.
|
|
25021
|
+
* @param resultLabel - Human-readable label used in formatted results.
|
|
25022
|
+
* @returns Async tool function compatible with commitment tool registration.
|
|
25023
|
+
*
|
|
25024
|
+
* @private internal helper for search-like commitments
|
|
25025
|
+
*/
|
|
25026
|
+
function createSerpSearchToolFunction(toolName, resultLabel) {
|
|
25027
|
+
return async (rawArgs) => {
|
|
25028
|
+
const { query, ...searchOptions } = rawArgs;
|
|
25029
|
+
if (typeof query !== 'string' || !query.trim()) {
|
|
25030
|
+
throw new Error(`${toolName} query is required`);
|
|
25031
|
+
}
|
|
25032
|
+
const searchEngine = new SerpSearchEngine();
|
|
25033
|
+
const results = await searchEngine.search(query, searchOptions);
|
|
25034
|
+
return _spaceTrim.spaceTrim((block) => `
|
|
25035
|
+
${resultLabel} results for "${query}"${Object.keys(searchOptions).length === 0
|
|
25036
|
+
? ''
|
|
25037
|
+
: ` with options ${JSON.stringify(searchOptions)}`}:
|
|
25038
|
+
|
|
25039
|
+
${block(results
|
|
25040
|
+
.map((result) => _spaceTrim.spaceTrim(`
|
|
25041
|
+
- **${result.title}**
|
|
25042
|
+
${result.url}
|
|
25043
|
+
${result.snippet}
|
|
25044
|
+
`))
|
|
25045
|
+
.join('\n\n'))}
|
|
25046
|
+
`);
|
|
25047
|
+
};
|
|
25048
|
+
}
|
|
25049
|
+
|
|
25050
|
+
/**
|
|
25051
|
+
* USE DEEPSEARCH commitment definition
|
|
25052
|
+
*
|
|
25053
|
+
* The `USE DEEPSEARCH` commitment indicates that the agent should use a deeper research-oriented
|
|
25054
|
+
* search workflow instead of lightweight web search when it needs fresh information from the internet.
|
|
25055
|
+
*
|
|
25056
|
+
* The content following `USE DEEPSEARCH` is an arbitrary text that the agent should know
|
|
25057
|
+
* (e.g. search scope or research instructions).
|
|
25058
|
+
*
|
|
25059
|
+
* Example usage in agent source:
|
|
25060
|
+
*
|
|
25061
|
+
* ```book
|
|
25062
|
+
* USE DEEPSEARCH
|
|
25063
|
+
* USE DEEPSEARCH Compare official vendor documentation with independent benchmarks.
|
|
25064
|
+
* ```
|
|
25065
|
+
*
|
|
25066
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
25067
|
+
*/
|
|
25068
|
+
class UseDeepSearchCommitmentDefinition extends BaseCommitmentDefinition {
|
|
25069
|
+
constructor() {
|
|
25070
|
+
super('USE DEEPSEARCH');
|
|
25071
|
+
}
|
|
25072
|
+
get requiresContent() {
|
|
25073
|
+
return false;
|
|
25074
|
+
}
|
|
25075
|
+
/**
|
|
25076
|
+
* Short one-line description of USE DEEPSEARCH.
|
|
25077
|
+
*/
|
|
25078
|
+
get description() {
|
|
25079
|
+
return 'Enable the agent to use DeepSearch for more thorough internet research.';
|
|
25080
|
+
}
|
|
25081
|
+
/**
|
|
25082
|
+
* Icon for this commitment.
|
|
25083
|
+
*/
|
|
25084
|
+
get icon() {
|
|
25085
|
+
return '🔬';
|
|
25086
|
+
}
|
|
25087
|
+
/**
|
|
25088
|
+
* Markdown documentation for USE DEEPSEARCH commitment.
|
|
25089
|
+
*/
|
|
25090
|
+
get documentation() {
|
|
25091
|
+
return _spaceTrim.spaceTrim(`
|
|
25092
|
+
# USE DEEPSEARCH
|
|
25093
|
+
|
|
25094
|
+
Enables the agent to use DeepSearch for broader, more thorough internet research than lightweight web search.
|
|
25095
|
+
|
|
25096
|
+
## Key aspects
|
|
25097
|
+
|
|
25098
|
+
- The content following \`USE DEEPSEARCH\` is arbitrary guidance for the research workflow.
|
|
25099
|
+
- In Agents Server, the OpenAI Agents SDK runtime uses a nested deep-research agent for this tool.
|
|
25100
|
+
- Use this for investigations, comparisons, market scans, or other tasks that benefit from deeper synthesis.
|
|
25101
|
+
- Prefer regular \`USE SEARCH ENGINE\` when a quick factual lookup is enough.
|
|
25102
|
+
|
|
25103
|
+
## Examples
|
|
25104
|
+
|
|
25105
|
+
\`\`\`book
|
|
25106
|
+
Due Diligence Researcher
|
|
25107
|
+
|
|
25108
|
+
GOAL Investigate vendors thoroughly before making recommendations.
|
|
25109
|
+
USE DEEPSEARCH Compare official sources with credible third-party analysis.
|
|
25110
|
+
RULE Cite the strongest supporting sources in the final answer.
|
|
25111
|
+
\`\`\`
|
|
25112
|
+
|
|
25113
|
+
\`\`\`book
|
|
25114
|
+
Market Analyst
|
|
25115
|
+
|
|
25116
|
+
GOAL Build concise but well-grounded research briefs.
|
|
25117
|
+
USE DEEPSEARCH Focus on recent public information and competing viewpoints.
|
|
25118
|
+
CLOSED
|
|
25119
|
+
\`\`\`
|
|
25120
|
+
`);
|
|
25121
|
+
}
|
|
25122
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
25123
|
+
const existingTools = requirements.tools || [];
|
|
25124
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'deep_search')
|
|
25125
|
+
? existingTools
|
|
25126
|
+
: [
|
|
25127
|
+
...existingTools,
|
|
25128
|
+
{
|
|
25129
|
+
name: 'deep_search',
|
|
25130
|
+
description: _spaceTrim.spaceTrim(`
|
|
25131
|
+
Research the internet deeply and synthesize a grounded answer.
|
|
25132
|
+
Use this tool for broader investigations, comparisons, and requests that need more than a quick search.
|
|
25133
|
+
`),
|
|
25134
|
+
parameters: {
|
|
25135
|
+
type: 'object',
|
|
25136
|
+
properties: {
|
|
25137
|
+
query: {
|
|
25138
|
+
type: 'string',
|
|
25139
|
+
description: 'The research question or investigation request.',
|
|
25140
|
+
},
|
|
25141
|
+
},
|
|
25142
|
+
required: ['query'],
|
|
25143
|
+
additionalProperties: false,
|
|
25144
|
+
},
|
|
25145
|
+
},
|
|
25146
|
+
];
|
|
25147
|
+
return appendAggregatedUseCommitmentPlaceholder({
|
|
25148
|
+
...requirements,
|
|
25149
|
+
tools: updatedTools,
|
|
25150
|
+
_metadata: {
|
|
25151
|
+
...requirements._metadata,
|
|
25152
|
+
useDeepSearch: content || true,
|
|
25153
|
+
},
|
|
25154
|
+
}, this.type);
|
|
25155
|
+
}
|
|
25156
|
+
/**
|
|
25157
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
25158
|
+
*/
|
|
25159
|
+
getToolTitles() {
|
|
25160
|
+
return {
|
|
25161
|
+
deep_search: 'DeepSearch',
|
|
25162
|
+
};
|
|
25163
|
+
}
|
|
25164
|
+
/**
|
|
25165
|
+
* Gets the local fallback implementation for the `deep_search` tool.
|
|
25166
|
+
*/
|
|
25167
|
+
getToolFunctions() {
|
|
25168
|
+
return {
|
|
25169
|
+
deep_search: createSerpSearchToolFunction('deep_search', 'DeepSearch'),
|
|
25170
|
+
};
|
|
25171
|
+
}
|
|
25172
|
+
}
|
|
25173
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
25174
|
+
|
|
24940
25175
|
/**
|
|
24941
25176
|
* Lightweight email token matcher used for `USE EMAIL` first-line parsing.
|
|
24942
25177
|
*
|
|
@@ -27324,49 +27559,6 @@
|
|
|
27324
27559
|
}
|
|
27325
27560
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
27326
27561
|
|
|
27327
|
-
/**
|
|
27328
|
-
* A search engine implementation that uses the SerpApi to fetch Google search results.
|
|
27329
|
-
*
|
|
27330
|
-
* @private <- TODO: !!!! Export via some package
|
|
27331
|
-
*/
|
|
27332
|
-
class SerpSearchEngine {
|
|
27333
|
-
get title() {
|
|
27334
|
-
return 'SerpApi Search Engine';
|
|
27335
|
-
}
|
|
27336
|
-
get description() {
|
|
27337
|
-
return 'Search engine that uses SerpApi to fetch Google search results';
|
|
27338
|
-
}
|
|
27339
|
-
checkConfiguration() {
|
|
27340
|
-
if (!process.env.SERP_API_KEY) {
|
|
27341
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
27342
|
-
}
|
|
27343
|
-
}
|
|
27344
|
-
async search(query, options = {}) {
|
|
27345
|
-
const apiKey = process.env.SERP_API_KEY;
|
|
27346
|
-
if (!apiKey) {
|
|
27347
|
-
throw new Error('SERP_API_KEY is not configured');
|
|
27348
|
-
}
|
|
27349
|
-
const url = new URL('https://serpapi.com/search');
|
|
27350
|
-
url.searchParams.set('api_key', apiKey);
|
|
27351
|
-
url.searchParams.set('engine', 'google');
|
|
27352
|
-
url.searchParams.set('q', query);
|
|
27353
|
-
for (const [key, value] of Object.entries(options)) {
|
|
27354
|
-
url.searchParams.set(key, String(value));
|
|
27355
|
-
}
|
|
27356
|
-
const response = await fetch(url.toString());
|
|
27357
|
-
if (!response.ok) {
|
|
27358
|
-
const body = await response.text();
|
|
27359
|
-
throw new Error(`SerpApi failed with status ${response.status}: ${response.statusText}\n${body}`);
|
|
27360
|
-
}
|
|
27361
|
-
const data = (await response.json());
|
|
27362
|
-
return (data.organic_results || []).map((item) => ({
|
|
27363
|
-
title: item.title,
|
|
27364
|
-
url: item.link,
|
|
27365
|
-
snippet: item.snippet || '',
|
|
27366
|
-
}));
|
|
27367
|
-
}
|
|
27368
|
-
}
|
|
27369
|
-
|
|
27370
27562
|
/**
|
|
27371
27563
|
* USE SEARCH ENGINE commitment definition
|
|
27372
27564
|
*
|
|
@@ -27510,26 +27702,7 @@
|
|
|
27510
27702
|
*/
|
|
27511
27703
|
getToolFunctions() {
|
|
27512
27704
|
return {
|
|
27513
|
-
|
|
27514
|
-
console.log('!!!! [Tool] web_search called', { args });
|
|
27515
|
-
const { query, ...options } = args;
|
|
27516
|
-
if (!query) {
|
|
27517
|
-
throw new Error('Search query is required');
|
|
27518
|
-
}
|
|
27519
|
-
const searchEngine = new SerpSearchEngine();
|
|
27520
|
-
const results = await searchEngine.search(query, options);
|
|
27521
|
-
return _spaceTrim.spaceTrim((block) => `
|
|
27522
|
-
Search results for "${query}"${Object.keys(options).length === 0 ? '' : ` with options ${JSON.stringify(options)}`}:
|
|
27523
|
-
|
|
27524
|
-
${block(results
|
|
27525
|
-
.map((result) => _spaceTrim.spaceTrim(`
|
|
27526
|
-
- **${result.title}**
|
|
27527
|
-
${result.url}
|
|
27528
|
-
${result.snippet}
|
|
27529
|
-
`))
|
|
27530
|
-
.join('\n\n'))}
|
|
27531
|
-
`);
|
|
27532
|
-
},
|
|
27705
|
+
web_search: createSerpSearchToolFunction('web_search', 'Search'),
|
|
27533
27706
|
};
|
|
27534
27707
|
}
|
|
27535
27708
|
}
|
|
@@ -29175,6 +29348,7 @@
|
|
|
29175
29348
|
new ClosedCommitmentDefinition(),
|
|
29176
29349
|
new TeamCommitmentDefinition(),
|
|
29177
29350
|
new UseBrowserCommitmentDefinition(),
|
|
29351
|
+
new UseDeepSearchCommitmentDefinition(),
|
|
29178
29352
|
new UseSearchEngineCommitmentDefinition(),
|
|
29179
29353
|
new UseSpawnCommitmentDefinition(),
|
|
29180
29354
|
new UseTimeoutCommitmentDefinition(),
|
|
@@ -42228,6 +42402,11 @@
|
|
|
42228
42402
|
label: 'Internet',
|
|
42229
42403
|
iconName: 'Search',
|
|
42230
42404
|
},
|
|
42405
|
+
'USE DEEPSEARCH': {
|
|
42406
|
+
type: 'search-engine',
|
|
42407
|
+
label: 'DeepSearch',
|
|
42408
|
+
iconName: 'Search',
|
|
42409
|
+
},
|
|
42231
42410
|
'USE TIME': {
|
|
42232
42411
|
type: 'time',
|
|
42233
42412
|
label: 'Time',
|
|
@@ -43291,6 +43470,246 @@
|
|
|
43291
43470
|
// TODO: Mirror from Collboard or some common package
|
|
43292
43471
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
43293
43472
|
|
|
43473
|
+
/**
|
|
43474
|
+
* Default file globs scanned for emoji tags.
|
|
43475
|
+
*/
|
|
43476
|
+
const DEFAULT_INCLUDE_GLOBS = ['**/*.{ts,tsx,js,jsx,json,md,txt}'];
|
|
43477
|
+
/**
|
|
43478
|
+
* Default ignored paths while scanning the repository.
|
|
43479
|
+
*/
|
|
43480
|
+
const DEFAULT_IGNORE_GLOBS = ['**/node_modules/**', '**/.git/**', '**/.promptbook/ptbk-coder/**'];
|
|
43481
|
+
/**
|
|
43482
|
+
* Directory used for Promptbook coder runtime caches.
|
|
43483
|
+
*/
|
|
43484
|
+
const PTBK_CODER_CACHE_DIRECTORY_PATH = '.promptbook/ptbk-coder';
|
|
43485
|
+
/**
|
|
43486
|
+
* Relative cache file path storing per-file emoji-tag scan results.
|
|
43487
|
+
*/
|
|
43488
|
+
const EMOJI_TAG_SCAN_CACHE_FILE_PATH = `${PTBK_CODER_CACHE_DIRECTORY_PATH}/emoji-tag-scan-cache.json`;
|
|
43489
|
+
/**
|
|
43490
|
+
* Current schema version of the persisted emoji-tag scan cache.
|
|
43491
|
+
*/
|
|
43492
|
+
const EMOJI_TAG_SCAN_CACHE_VERSION = 1;
|
|
43493
|
+
/**
|
|
43494
|
+
* Scans repository files for bracketed emoji tags while reusing per-file cache entries for unchanged files.
|
|
43495
|
+
*/
|
|
43496
|
+
async function scanEmojiTagUsage(options) {
|
|
43497
|
+
var _a, _b, _c, _d;
|
|
43498
|
+
const rootDir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
|
|
43499
|
+
const includeGlobs = (_b = options.includeGlobs) !== null && _b !== void 0 ? _b : DEFAULT_INCLUDE_GLOBS;
|
|
43500
|
+
const ignoreGlobs = (_c = options.ignoreGlobs) !== null && _c !== void 0 ? _c : DEFAULT_IGNORE_GLOBS;
|
|
43501
|
+
const tagPrefix = (_d = options.tagPrefix) !== null && _d !== void 0 ? _d : '';
|
|
43502
|
+
const filesToScan = await findFilesToScan(rootDir, includeGlobs, ignoreGlobs);
|
|
43503
|
+
const matcher = buildEmojiTagMatcher(options.candidateEmojis, tagPrefix);
|
|
43504
|
+
const usedEmojis = new Set();
|
|
43505
|
+
const existingCache = await readEmojiTagScanCache(rootDir);
|
|
43506
|
+
const nextCacheFiles = { ...existingCache.files };
|
|
43507
|
+
let isCacheDirty = false;
|
|
43508
|
+
let scannedFileCount = 0;
|
|
43509
|
+
let reusedFileCount = 0;
|
|
43510
|
+
for (const filePath of filesToScan) {
|
|
43511
|
+
try {
|
|
43512
|
+
const fileStats = fs.statSync(filePath);
|
|
43513
|
+
const cacheKey = toCacheKey(rootDir, filePath);
|
|
43514
|
+
const cachedFile = nextCacheFiles[cacheKey];
|
|
43515
|
+
const cachedEmojis = getCachedFileEmojis(cachedFile, fileStats.mtimeMs, fileStats.size, tagPrefix);
|
|
43516
|
+
if (cachedEmojis) {
|
|
43517
|
+
reusedFileCount += 1;
|
|
43518
|
+
addEmojis(usedEmojis, cachedEmojis);
|
|
43519
|
+
continue;
|
|
43520
|
+
}
|
|
43521
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8'); /* Note: sync file reads are fine for local tooling. */
|
|
43522
|
+
const scannedEmojis = scanFileForEmojiTags(fileContent, matcher);
|
|
43523
|
+
addEmojis(usedEmojis, scannedEmojis);
|
|
43524
|
+
nextCacheFiles[cacheKey] = updateCachedFile(cachedFile, fileStats.mtimeMs, fileStats.size, tagPrefix, scannedEmojis);
|
|
43525
|
+
scannedFileCount += 1;
|
|
43526
|
+
isCacheDirty = true;
|
|
43527
|
+
}
|
|
43528
|
+
catch (error) {
|
|
43529
|
+
if (options.onFileError) {
|
|
43530
|
+
options.onFileError(normalizeError(error), filePath);
|
|
43531
|
+
continue;
|
|
43532
|
+
}
|
|
43533
|
+
throw error;
|
|
43534
|
+
}
|
|
43535
|
+
}
|
|
43536
|
+
if (isCacheDirty) {
|
|
43537
|
+
await writeEmojiTagScanCache(rootDir, {
|
|
43538
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43539
|
+
files: nextCacheFiles,
|
|
43540
|
+
});
|
|
43541
|
+
}
|
|
43542
|
+
return {
|
|
43543
|
+
usedEmojis,
|
|
43544
|
+
scannedFileCount,
|
|
43545
|
+
reusedFileCount,
|
|
43546
|
+
};
|
|
43547
|
+
}
|
|
43548
|
+
/**
|
|
43549
|
+
* Resolves files to scan for matching emoji tags.
|
|
43550
|
+
*/
|
|
43551
|
+
async function findFilesToScan(rootDir, includeGlobs, ignoreGlobs) {
|
|
43552
|
+
const files = new Set();
|
|
43553
|
+
for (const pattern of includeGlobs) {
|
|
43554
|
+
const matches = await glob__default["default"](pattern, {
|
|
43555
|
+
cwd: rootDir,
|
|
43556
|
+
ignore: Array.from(new Set([...ignoreGlobs, ...DEFAULT_IGNORE_GLOBS])),
|
|
43557
|
+
nodir: true,
|
|
43558
|
+
absolute: true,
|
|
43559
|
+
});
|
|
43560
|
+
for (const match of matches) {
|
|
43561
|
+
files.add(match);
|
|
43562
|
+
}
|
|
43563
|
+
}
|
|
43564
|
+
return Array.from(files);
|
|
43565
|
+
}
|
|
43566
|
+
/**
|
|
43567
|
+
* Builds a regex that matches one exact bracketed emoji-tag form.
|
|
43568
|
+
*/
|
|
43569
|
+
function buildEmojiTagMatcher(candidateEmojis, tagPrefix) {
|
|
43570
|
+
const escapedEmojiAlternatives = Array.from(candidateEmojis)
|
|
43571
|
+
.sort((leftEmoji, rightEmoji) => rightEmoji.length - leftEmoji.length)
|
|
43572
|
+
.map((emoji) => escapeRegExp$1(emoji))
|
|
43573
|
+
.join('|');
|
|
43574
|
+
if (escapedEmojiAlternatives === '') {
|
|
43575
|
+
return /$^/u;
|
|
43576
|
+
}
|
|
43577
|
+
return new RegExp(`\\[${escapeRegExp$1(tagPrefix)}(?<emoji>${escapedEmojiAlternatives})\\]`, 'gu');
|
|
43578
|
+
}
|
|
43579
|
+
/**
|
|
43580
|
+
* Extracts matching emojis from one file content.
|
|
43581
|
+
*/
|
|
43582
|
+
function scanFileForEmojiTags(fileContent, matcher) {
|
|
43583
|
+
var _a;
|
|
43584
|
+
matcher.lastIndex = 0;
|
|
43585
|
+
const matchedEmojis = new Set();
|
|
43586
|
+
for (const match of fileContent.matchAll(matcher)) {
|
|
43587
|
+
const emoji = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.emoji;
|
|
43588
|
+
if (emoji) {
|
|
43589
|
+
matchedEmojis.add(emoji);
|
|
43590
|
+
}
|
|
43591
|
+
}
|
|
43592
|
+
return Array.from(matchedEmojis);
|
|
43593
|
+
}
|
|
43594
|
+
/**
|
|
43595
|
+
* Returns cached emojis when the file metadata still matches the stored cache entry.
|
|
43596
|
+
*/
|
|
43597
|
+
function getCachedFileEmojis(cachedFile, mtimeMs, size, tagPrefix) {
|
|
43598
|
+
if (!cachedFile || cachedFile.mtimeMs !== mtimeMs || cachedFile.size !== size) {
|
|
43599
|
+
return undefined;
|
|
43600
|
+
}
|
|
43601
|
+
const cachedEmojis = cachedFile.tagsByPrefix[tagPrefix];
|
|
43602
|
+
return Array.isArray(cachedEmojis) ? cachedEmojis : undefined;
|
|
43603
|
+
}
|
|
43604
|
+
/**
|
|
43605
|
+
* Stores the latest scan result for one file while preserving other prefix caches when the file revision is unchanged.
|
|
43606
|
+
*/
|
|
43607
|
+
function updateCachedFile(cachedFile, mtimeMs, size, tagPrefix, emojis) {
|
|
43608
|
+
const isSameFileRevision = (cachedFile === null || cachedFile === void 0 ? void 0 : cachedFile.mtimeMs) === mtimeMs && cachedFile.size === size;
|
|
43609
|
+
return {
|
|
43610
|
+
mtimeMs,
|
|
43611
|
+
size,
|
|
43612
|
+
tagsByPrefix: {
|
|
43613
|
+
...(isSameFileRevision ? cachedFile.tagsByPrefix : {}),
|
|
43614
|
+
[tagPrefix]: [...emojis],
|
|
43615
|
+
},
|
|
43616
|
+
};
|
|
43617
|
+
}
|
|
43618
|
+
/**
|
|
43619
|
+
* Loads the persisted emoji-tag scan cache and falls back to an empty cache when it is missing or invalid.
|
|
43620
|
+
*/
|
|
43621
|
+
async function readEmojiTagScanCache(rootDir) {
|
|
43622
|
+
try {
|
|
43623
|
+
const cacheContent = await promises.readFile(path.join(rootDir, EMOJI_TAG_SCAN_CACHE_FILE_PATH), 'utf-8');
|
|
43624
|
+
return normalizeEmojiTagScanCache(JSON.parse(cacheContent));
|
|
43625
|
+
}
|
|
43626
|
+
catch (_a) {
|
|
43627
|
+
return createEmptyEmojiTagScanCache();
|
|
43628
|
+
}
|
|
43629
|
+
}
|
|
43630
|
+
/**
|
|
43631
|
+
* Persists the updated emoji-tag scan cache as a best-effort optimization.
|
|
43632
|
+
*/
|
|
43633
|
+
async function writeEmojiTagScanCache(rootDir, cache) {
|
|
43634
|
+
try {
|
|
43635
|
+
await promises.mkdir(path.join(rootDir, PTBK_CODER_CACHE_DIRECTORY_PATH), { recursive: true });
|
|
43636
|
+
await promises.writeFile(path.join(rootDir, EMOJI_TAG_SCAN_CACHE_FILE_PATH), `${JSON.stringify(cache, null, 2)}\n`, 'utf-8');
|
|
43637
|
+
}
|
|
43638
|
+
catch (_a) {
|
|
43639
|
+
// Note: Cache writes are only an optimization; scanning still succeeds when the cache cannot be written.
|
|
43640
|
+
}
|
|
43641
|
+
}
|
|
43642
|
+
/**
|
|
43643
|
+
* Normalizes one parsed cache payload into the current typed cache shape.
|
|
43644
|
+
*/
|
|
43645
|
+
function normalizeEmojiTagScanCache(value) {
|
|
43646
|
+
if (!isPlainObject(value) || value.version !== EMOJI_TAG_SCAN_CACHE_VERSION || !isPlainObject(value.files)) {
|
|
43647
|
+
return createEmptyEmojiTagScanCache();
|
|
43648
|
+
}
|
|
43649
|
+
const files = {};
|
|
43650
|
+
for (const [filePath, cachedValue] of Object.entries(value.files)) {
|
|
43651
|
+
if (!isPlainObject(cachedValue)) {
|
|
43652
|
+
continue;
|
|
43653
|
+
}
|
|
43654
|
+
const { mtimeMs, size, tagsByPrefix } = cachedValue;
|
|
43655
|
+
if (typeof mtimeMs !== 'number' || typeof size !== 'number' || !isPlainObject(tagsByPrefix)) {
|
|
43656
|
+
continue;
|
|
43657
|
+
}
|
|
43658
|
+
const normalizedTagsByPrefix = {};
|
|
43659
|
+
for (const [tagPrefix, cachedEmojis] of Object.entries(tagsByPrefix)) {
|
|
43660
|
+
if (!Array.isArray(cachedEmojis)) {
|
|
43661
|
+
continue;
|
|
43662
|
+
}
|
|
43663
|
+
normalizedTagsByPrefix[tagPrefix] = cachedEmojis.filter((emoji) => typeof emoji === 'string');
|
|
43664
|
+
}
|
|
43665
|
+
files[filePath] = {
|
|
43666
|
+
mtimeMs,
|
|
43667
|
+
size,
|
|
43668
|
+
tagsByPrefix: normalizedTagsByPrefix,
|
|
43669
|
+
};
|
|
43670
|
+
}
|
|
43671
|
+
return {
|
|
43672
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43673
|
+
files,
|
|
43674
|
+
};
|
|
43675
|
+
}
|
|
43676
|
+
/**
|
|
43677
|
+
* Creates an empty cache payload for emoji-tag scans.
|
|
43678
|
+
*/
|
|
43679
|
+
function createEmptyEmojiTagScanCache() {
|
|
43680
|
+
return {
|
|
43681
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43682
|
+
files: {},
|
|
43683
|
+
};
|
|
43684
|
+
}
|
|
43685
|
+
/**
|
|
43686
|
+
* Converts an absolute file path into a stable cache key relative to the scanned root.
|
|
43687
|
+
*/
|
|
43688
|
+
function toCacheKey(rootDir, filePath) {
|
|
43689
|
+
return path.relative(rootDir, filePath).replace(/\\/gu, '/');
|
|
43690
|
+
}
|
|
43691
|
+
/**
|
|
43692
|
+
* Adds multiple emojis into one target set.
|
|
43693
|
+
*/
|
|
43694
|
+
function addEmojis(target, emojis) {
|
|
43695
|
+
for (const emoji of emojis) {
|
|
43696
|
+
target.add(emoji);
|
|
43697
|
+
}
|
|
43698
|
+
}
|
|
43699
|
+
/**
|
|
43700
|
+
* Checks whether one unknown JSON value is a plain object.
|
|
43701
|
+
*/
|
|
43702
|
+
function isPlainObject(value) {
|
|
43703
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43704
|
+
}
|
|
43705
|
+
/**
|
|
43706
|
+
* Normalizes thrown values into proper `Error` objects for optional callbacks.
|
|
43707
|
+
*/
|
|
43708
|
+
function normalizeError(error) {
|
|
43709
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
43710
|
+
}
|
|
43711
|
+
// Note: [?] Code in this file should never be published in any package
|
|
43712
|
+
|
|
43294
43713
|
// find-fresh-emoji-tags.ts
|
|
43295
43714
|
// Note: When run as a standalone script, call the exported function
|
|
43296
43715
|
if (require.main === module) {
|
|
@@ -43320,29 +43739,16 @@
|
|
|
43320
43739
|
async function findFreshEmojiTag() {
|
|
43321
43740
|
initializeFindFreshEmojiTagRun();
|
|
43322
43741
|
console.info(`🤪 Find fresh emoji tag`);
|
|
43323
|
-
const allFiles = await glob__default["default"]('**/*.{ts,tsx,js,jsx,json,md,txt}', {
|
|
43324
|
-
ignore: '**/node_modules/**', // <- TODO: [🚰] Ignore also hidden folders like *(`.promptbook`, `.next`, `.git`,...)*
|
|
43325
|
-
});
|
|
43326
43742
|
const allEmojis = EMOJIS_OF_SINGLE_PICTOGRAM;
|
|
43327
|
-
|
|
43328
|
-
|
|
43329
|
-
|
|
43330
|
-
|
|
43331
|
-
|
|
43332
|
-
for (const emoji of allEmojis) {
|
|
43333
|
-
const tag = `[${emoji}]`;
|
|
43334
|
-
if (content.includes(tag)) {
|
|
43335
|
-
usedEmojis.add(emoji);
|
|
43336
|
-
}
|
|
43337
|
-
}
|
|
43338
|
-
}
|
|
43339
|
-
catch (error) {
|
|
43340
|
-
console.error(colors__default["default"].red('Error in checking file file /' + file));
|
|
43743
|
+
const { usedEmojis } = await scanEmojiTagUsage({
|
|
43744
|
+
candidateEmojis: allEmojis,
|
|
43745
|
+
tagPrefix: '',
|
|
43746
|
+
onFileError: (error, filePath) => {
|
|
43747
|
+
console.error(colors__default["default"].red('Error in checking file /' + filePath));
|
|
43341
43748
|
console.error(error);
|
|
43342
|
-
}
|
|
43343
|
-
}
|
|
43344
|
-
|
|
43345
|
-
const freshEmojis = difference(allEmojis, usedEmojis);
|
|
43749
|
+
},
|
|
43750
|
+
});
|
|
43751
|
+
const freshEmojis = new Set(Array.from(allEmojis).filter((emoji) => !usedEmojis.has(emoji)));
|
|
43346
43752
|
console.info(colors__default["default"].green(`Avialable fresh tags:`));
|
|
43347
43753
|
const randomEmojis = [...$shuffleItems(...Array.from(freshEmojis))].splice(0, 10);
|
|
43348
43754
|
// const randomEmojis = freshEmojis;
|
|
@@ -43950,9 +44356,14 @@
|
|
|
43950
44356
|
const includeGlobs = (_b = options.includeGlobs) !== null && _b !== void 0 ? _b : ['**/*.{ts,tsx,js,jsx,json,md,txt}'];
|
|
43951
44357
|
const ignoreGlobs = (_c = options.ignoreGlobs) !== null && _c !== void 0 ? _c : ['**/node_modules/**'];
|
|
43952
44358
|
const tagPrefix = (_d = options.tagPrefix) !== null && _d !== void 0 ? _d : PROMPT_EMOJI_TAG_PREFIX;
|
|
43953
|
-
const
|
|
43954
|
-
|
|
43955
|
-
|
|
44359
|
+
const { usedEmojis } = await scanEmojiTagUsage({
|
|
44360
|
+
rootDir,
|
|
44361
|
+
includeGlobs,
|
|
44362
|
+
ignoreGlobs,
|
|
44363
|
+
tagPrefix,
|
|
44364
|
+
candidateEmojis: EMOJIS_OF_SINGLE_PICTOGRAM,
|
|
44365
|
+
});
|
|
44366
|
+
const freshEmojis = new Set(Array.from(EMOJIS_OF_SINGLE_PICTOGRAM).filter((emoji) => !usedEmojis.has(emoji)));
|
|
43956
44367
|
const shuffledEmojis = $shuffleItems(...Array.from(freshEmojis));
|
|
43957
44368
|
const selectedEmojis = shuffledEmojis.slice(0, count);
|
|
43958
44369
|
if (selectedEmojis.length < count) {
|
|
@@ -43964,40 +44375,6 @@
|
|
|
43964
44375
|
tagPrefix,
|
|
43965
44376
|
};
|
|
43966
44377
|
}
|
|
43967
|
-
/**
|
|
43968
|
-
* Resolves files to scan for existing prompt emoji tags.
|
|
43969
|
-
*/
|
|
43970
|
-
async function findFilesToScan(rootDir, includeGlobs, ignoreGlobs) {
|
|
43971
|
-
const files = new Set();
|
|
43972
|
-
for (const pattern of includeGlobs) {
|
|
43973
|
-
const matches = await glob__default["default"](pattern, {
|
|
43974
|
-
cwd: rootDir,
|
|
43975
|
-
ignore: ignoreGlobs,
|
|
43976
|
-
nodir: true,
|
|
43977
|
-
absolute: true,
|
|
43978
|
-
});
|
|
43979
|
-
for (const match of matches) {
|
|
43980
|
-
files.add(match);
|
|
43981
|
-
}
|
|
43982
|
-
}
|
|
43983
|
-
return Array.from(files);
|
|
43984
|
-
}
|
|
43985
|
-
/**
|
|
43986
|
-
* Collects emojis already used with the configured prompt prefix.
|
|
43987
|
-
*/
|
|
43988
|
-
function collectUsedPromptEmojis(filePaths, tagPrefix) {
|
|
43989
|
-
const usedEmojis = new Set();
|
|
43990
|
-
for (const file of filePaths) {
|
|
43991
|
-
const content = fs.readFileSync(file, 'utf-8'); /* Note: sync read is fine for script tooling. */
|
|
43992
|
-
for (const emoji of EMOJIS_OF_SINGLE_PICTOGRAM) {
|
|
43993
|
-
const tag = formatPromptEmojiTag(emoji, tagPrefix);
|
|
43994
|
-
if (content.includes(tag)) {
|
|
43995
|
-
usedEmojis.add(emoji);
|
|
43996
|
-
}
|
|
43997
|
-
}
|
|
43998
|
-
}
|
|
43999
|
-
return usedEmojis;
|
|
44000
|
-
}
|
|
44001
44378
|
// Note: [?] Code in this file should never be published in any package
|
|
44002
44379
|
|
|
44003
44380
|
var promptEmojiTags = /*#__PURE__*/Object.freeze({
|
|
@@ -45026,6 +45403,27 @@
|
|
|
45026
45403
|
* Current pause state.
|
|
45027
45404
|
*/
|
|
45028
45405
|
let pauseState = 'RUNNING';
|
|
45406
|
+
/**
|
|
45407
|
+
* Stores one new pause state in the shared runner controller.
|
|
45408
|
+
*/
|
|
45409
|
+
function setPauseState(nextPauseState) {
|
|
45410
|
+
pauseState = nextPauseState;
|
|
45411
|
+
}
|
|
45412
|
+
/**
|
|
45413
|
+
* Applies the same three-state toggle used by the `P` hotkey.
|
|
45414
|
+
*/
|
|
45415
|
+
function togglePauseState() {
|
|
45416
|
+
if (pauseState === 'RUNNING') {
|
|
45417
|
+
setPauseState('PAUSING');
|
|
45418
|
+
return 'REQUESTED_PAUSE';
|
|
45419
|
+
}
|
|
45420
|
+
if (pauseState === 'PAUSING') {
|
|
45421
|
+
setPauseState('RUNNING');
|
|
45422
|
+
return 'CANCELLED_PAUSE';
|
|
45423
|
+
}
|
|
45424
|
+
setPauseState('RUNNING');
|
|
45425
|
+
return 'RESUMED';
|
|
45426
|
+
}
|
|
45029
45427
|
/**
|
|
45030
45428
|
* Listens for the "p" key to pause and resume.
|
|
45031
45429
|
*/
|
|
@@ -45040,19 +45438,13 @@
|
|
|
45040
45438
|
process.exit();
|
|
45041
45439
|
}
|
|
45042
45440
|
if (key.name === 'p') {
|
|
45043
|
-
|
|
45044
|
-
|
|
45441
|
+
const toggleResult = togglePauseState();
|
|
45442
|
+
if (toggleResult === 'REQUESTED_PAUSE') {
|
|
45045
45443
|
// Note: Using console.log here which adds a new line.
|
|
45046
45444
|
// This is intentional to prevent the message from being overwritten.
|
|
45047
45445
|
console.log(colors__default["default"].bgWhite('Pausing...'));
|
|
45048
45446
|
}
|
|
45049
|
-
else if (
|
|
45050
|
-
pauseState = 'RUNNING';
|
|
45051
|
-
// The checkPause loop will terminate.
|
|
45052
|
-
}
|
|
45053
|
-
else if (pauseState === 'PAUSING') {
|
|
45054
|
-
// If user presses 'p' again while pausing, cancel the pause.
|
|
45055
|
-
pauseState = 'RUNNING';
|
|
45447
|
+
else if (toggleResult === 'CANCELLED_PAUSE') {
|
|
45056
45448
|
console.log(colors__default["default"].green('Pause cancelled. Resuming...'));
|
|
45057
45449
|
}
|
|
45058
45450
|
}
|
|
@@ -45068,12 +45460,12 @@
|
|
|
45068
45460
|
async function checkPause(options) {
|
|
45069
45461
|
var _a, _b;
|
|
45070
45462
|
if (pauseState === 'PAUSING') {
|
|
45071
|
-
|
|
45463
|
+
setPauseState('PAUSED');
|
|
45072
45464
|
if (!(options === null || options === void 0 ? void 0 : options.silent)) {
|
|
45073
45465
|
console.log(colors__default["default"].bgWhite.black('Paused') + colors__default["default"].gray(' (Press "p" to resume)'));
|
|
45074
45466
|
}
|
|
45075
45467
|
(_a = options === null || options === void 0 ? void 0 : options.onPaused) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
45076
|
-
while (
|
|
45468
|
+
while (getPauseState() === 'PAUSED') {
|
|
45077
45469
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
45078
45470
|
}
|
|
45079
45471
|
(_b = options === null || options === void 0 ? void 0 : options.onResumed) === null || _b === void 0 ? void 0 : _b.call(options);
|
|
@@ -45088,20 +45480,6 @@
|
|
|
45088
45480
|
function getPauseState() {
|
|
45089
45481
|
return pauseState;
|
|
45090
45482
|
}
|
|
45091
|
-
/**
|
|
45092
|
-
* Requests a pause from an external controller (e.g. the Ink UI).
|
|
45093
|
-
*/
|
|
45094
|
-
function requestPause() {
|
|
45095
|
-
if (pauseState === 'RUNNING') {
|
|
45096
|
-
pauseState = 'PAUSING';
|
|
45097
|
-
}
|
|
45098
|
-
}
|
|
45099
|
-
/**
|
|
45100
|
-
* Resumes execution from an external controller after a pause.
|
|
45101
|
-
*/
|
|
45102
|
-
function requestResume() {
|
|
45103
|
-
pauseState = 'RUNNING';
|
|
45104
|
-
}
|
|
45105
45483
|
|
|
45106
45484
|
/**
|
|
45107
45485
|
* Environment variable that configures the name used for agent commits.
|
|
@@ -48741,49 +49119,215 @@ bash "$1"
|
|
|
48741
49119
|
return `${scriptPath}.test.sh`;
|
|
48742
49120
|
}
|
|
48743
49121
|
|
|
49122
|
+
/**
|
|
49123
|
+
* Centers an ANSI-colored line within the available frame width.
|
|
49124
|
+
*
|
|
49125
|
+
* @private internal utility of coder run UI
|
|
49126
|
+
*/
|
|
49127
|
+
function centerAnsiText(text, width) {
|
|
49128
|
+
const paddingWidth = Math.max(0, Math.floor((width - visibleLength(text)) / 2));
|
|
49129
|
+
return `${' '.repeat(paddingWidth)}${text}`;
|
|
49130
|
+
}
|
|
49131
|
+
/**
|
|
49132
|
+
* Pads or truncates a possibly ANSI-colored line to the target visible width.
|
|
49133
|
+
*
|
|
49134
|
+
* @private internal utility of coder run UI
|
|
49135
|
+
*/
|
|
49136
|
+
function padAnsiText(text, width) {
|
|
49137
|
+
const fittedText = fitAnsiText(text, width);
|
|
49138
|
+
return fittedText + ' '.repeat(Math.max(0, width - visibleLength(fittedText)));
|
|
49139
|
+
}
|
|
49140
|
+
/**
|
|
49141
|
+
* Truncates a possibly ANSI-colored line to the target visible width.
|
|
49142
|
+
*
|
|
49143
|
+
* @private internal utility of coder run UI
|
|
49144
|
+
*/
|
|
49145
|
+
function fitAnsiText(text, width) {
|
|
49146
|
+
if (visibleLength(text) <= width) {
|
|
49147
|
+
return text;
|
|
49148
|
+
}
|
|
49149
|
+
return fitPlainText(stripAnsi(text), width);
|
|
49150
|
+
}
|
|
49151
|
+
/**
|
|
49152
|
+
* Truncates a plain-text line to the target width with an ellipsis.
|
|
49153
|
+
*
|
|
49154
|
+
* @private internal utility of coder run UI
|
|
49155
|
+
*/
|
|
49156
|
+
function fitPlainText(text, width) {
|
|
49157
|
+
if (text.length <= width) {
|
|
49158
|
+
return text;
|
|
49159
|
+
}
|
|
49160
|
+
if (width <= 3) {
|
|
49161
|
+
return '.'.repeat(width);
|
|
49162
|
+
}
|
|
49163
|
+
return `${text.slice(0, width - 3)}...`;
|
|
49164
|
+
}
|
|
49165
|
+
/**
|
|
49166
|
+
* Measures visible string width by stripping ANSI escape codes.
|
|
49167
|
+
*
|
|
49168
|
+
* @private internal utility of coder run UI
|
|
49169
|
+
*/
|
|
49170
|
+
function visibleLength(text) {
|
|
49171
|
+
return stripAnsi(text).length;
|
|
49172
|
+
}
|
|
49173
|
+
/**
|
|
49174
|
+
* Strips ANSI escape codes from a string.
|
|
49175
|
+
*
|
|
49176
|
+
* @private internal utility of coder run UI
|
|
49177
|
+
*/
|
|
49178
|
+
function stripAnsi(text) {
|
|
49179
|
+
// eslint-disable-next-line no-control-regex
|
|
49180
|
+
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
49181
|
+
}
|
|
49182
|
+
|
|
49183
|
+
/**
|
|
49184
|
+
* Fixed left-side head used across all octopus frames.
|
|
49185
|
+
*/
|
|
49186
|
+
const OCTOPUS_HEAD_LINES = [
|
|
49187
|
+
colors__default["default"].magenta.bold(' .-""""-.'),
|
|
49188
|
+
colors__default["default"].magenta.bold(" .' .-. '."),
|
|
49189
|
+
`${colors__default["default"].magenta.bold(' / (')}${colors__default["default"].yellow.bold('o o')}${colors__default["default"].magenta.bold(') \\')}`,
|
|
49190
|
+
colors__default["default"].magenta.bold(' | ^ |'),
|
|
49191
|
+
colors__default["default"].magenta.bold(' | \\___/ |'),
|
|
49192
|
+
colors__default["default"].magenta.bold(' \\___________/'),
|
|
49193
|
+
];
|
|
49194
|
+
/**
|
|
49195
|
+
* Gap between the head silhouette and the animated tentacles.
|
|
49196
|
+
*/
|
|
49197
|
+
const OCTOPUS_HEAD_TO_TENTACLE_GAP = ' ';
|
|
49198
|
+
/**
|
|
49199
|
+
* Animated right-side tentacle poses.
|
|
49200
|
+
*/
|
|
49201
|
+
const OCTOPUS_TENTACLE_FRAMES = [
|
|
49202
|
+
[
|
|
49203
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49204
|
+
colors__default["default"].cyan(' __ __'),
|
|
49205
|
+
colors__default["default"].cyan(' _/\\/\\/ \\_/\\/ \\_/\\_'),
|
|
49206
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/\\__'),
|
|
49207
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_/ \\_/'),
|
|
49208
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/ /'),
|
|
49209
|
+
],
|
|
49210
|
+
[
|
|
49211
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49212
|
+
colors__default["default"].cyan(' __ __'),
|
|
49213
|
+
colors__default["default"].cyan(' _/\\/ \\_/\\/\\_ \\_/\\_'),
|
|
49214
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ \\/\\/ \\__'),
|
|
49215
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_ /\\_/'),
|
|
49216
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/ /'),
|
|
49217
|
+
],
|
|
49218
|
+
[
|
|
49219
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49220
|
+
colors__default["default"].cyan(' __ __'),
|
|
49221
|
+
colors__default["default"].cyan(' _/\\/\\/ \\_/\\_ \\_/\\_'),
|
|
49222
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/ \\_'),
|
|
49223
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_/ _/'),
|
|
49224
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/'),
|
|
49225
|
+
],
|
|
49226
|
+
[
|
|
49227
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49228
|
+
colors__default["default"].cyan(' __ __'),
|
|
49229
|
+
colors__default["default"].cyan(' _/\\/ \\_/\\/ \\_/\\/\\_'),
|
|
49230
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/\\ \\'),
|
|
49231
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_ \\_/'),
|
|
49232
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__\\_/'),
|
|
49233
|
+
],
|
|
49234
|
+
];
|
|
49235
|
+
/**
|
|
49236
|
+
* Builds the horizontal octopus illustration shown above the coder-run dashboard.
|
|
49237
|
+
*
|
|
49238
|
+
* @private internal utility of coder run UI
|
|
49239
|
+
*/
|
|
49240
|
+
function buildCoderRunOctopusVisual(options) {
|
|
49241
|
+
const tentacleFrame = OCTOPUS_TENTACLE_FRAMES[((options.animationFrame % OCTOPUS_TENTACLE_FRAMES.length) + OCTOPUS_TENTACLE_FRAMES.length) %
|
|
49242
|
+
OCTOPUS_TENTACLE_FRAMES.length];
|
|
49243
|
+
const visualLines = OCTOPUS_HEAD_LINES.map((headLine, lineIndex) => `${headLine}${OCTOPUS_HEAD_TO_TENTACLE_GAP}${tentacleFrame[lineIndex]}`);
|
|
49244
|
+
const visualWidth = visualLines.reduce((maxWidth, line) => Math.max(maxWidth, visibleLength(line)), 0);
|
|
49245
|
+
visualLines.map((line) => centerAnsiText(padAnsiText(line, visualWidth), options.totalWidth));
|
|
49246
|
+
/*
|
|
49247
|
+
Note: Octopus art should look better, now using just text
|
|
49248
|
+
https://www.google.com/search?sca_esv=52e84acb78c558cc&sxsrf=ANbL-n7DVKf71T1HSPRpM-2skfMss0jh7w:1776693767588&udm=2&fbs=ADc_l-ZseckkBJUFopaGDNYa-HGjo4_b6b_a7pIHTL5Y9QnExg6xJqXbG7aOLcH8CWqOtkzCrjxXWZVmrIhYPvZzFDVUIb7oTJfuJ6idsCc5GA1j5KGoi2q3sW0uDBWWfYgbuxGWTQPZMetvj33BdP833wZm47mxW-6rC3bTQWluwJdOsgloPieyQvTfF2uNgIZ_K0KZ-WzpL1An8GuRrKqHdvl8T306FA&q=octopus&sa=X&ved=2ahUKEwil7ZCHzPyTAxUghP0HHY-_Js8QtKgLegQIOhAB&biw=1745&bih=903&dpr=1.1#sv=CAMSVhoyKhBlLVl1bHNmeVhneml6a1dNMg5ZdWxzZnlYZ3ppemtXTToOTVJvdXhTdk5STkZ4d00gBCocCgZtb3NhaWMSEGUtWXVsc2Z5WGd6aXprV00YADABGAcg46LBxA9KCBABGAEgASgB
|
|
49249
|
+
https://www.mrgoodfish.com/wp-content/uploads/2022/09/Eledone_moschata__.png
|
|
49250
|
+
https://fish-commercial-names.ec.europa.eu/fish-names/jakarta.faces.resource/pictograms/octopus_vulgaris.jpg.xhtml?ln=images
|
|
49251
|
+
https://www.asciiart.eu/image-to-ascii
|
|
49252
|
+
*/
|
|
49253
|
+
// Note: Created by https://patorjk.com/software/taag/#p=display&f=ANSI+Compact&t=ptbk.io&x=none&v=4&h=4&w=80&we=false
|
|
49254
|
+
return _spaceTrim__default["default"](`
|
|
49255
|
+
|
|
49256
|
+
▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄
|
|
49257
|
+
██▄█▀ ██ ██▄██ ██▄█▀ ██ ██▀██
|
|
49258
|
+
██ ██ ██▄█▀ ██ ██ ▄ ██ ▀███▀
|
|
49259
|
+
|
|
49260
|
+
`)
|
|
49261
|
+
.split('\n')
|
|
49262
|
+
.map((line) => centerAnsiText(line, options.totalWidth));
|
|
49263
|
+
}
|
|
49264
|
+
|
|
49265
|
+
/**
|
|
49266
|
+
* Refresh cadence used only while the rich coder UI needs animated updates.
|
|
49267
|
+
*
|
|
49268
|
+
* @private internal constant of coder run UI
|
|
49269
|
+
*/
|
|
49270
|
+
const ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS = 300;
|
|
49271
|
+
/**
|
|
49272
|
+
* Phases that still benefit from automatic refreshes because the frame can change
|
|
49273
|
+
* over time even without new runner output.
|
|
49274
|
+
*
|
|
49275
|
+
* @private internal constant of coder run UI
|
|
49276
|
+
*/
|
|
49277
|
+
const AUTO_REFRESH_PHASES = ['initializing', 'loading', 'running', 'verifying'];
|
|
49278
|
+
/**
|
|
49279
|
+
* Returns whether the rich coder UI should keep animating on its own.
|
|
49280
|
+
*
|
|
49281
|
+
* @private internal utility of coder run UI
|
|
49282
|
+
*/
|
|
49283
|
+
function isCoderRunUiAutoRefreshing(phase, pauseState) {
|
|
49284
|
+
// `PAUSING` still means the current task is winding down, so keep active
|
|
49285
|
+
// animations/timers running until the runner reaches the fully paused state.
|
|
49286
|
+
if (pauseState === 'PAUSED') {
|
|
49287
|
+
return false;
|
|
49288
|
+
}
|
|
49289
|
+
return AUTO_REFRESH_PHASES.includes(phase);
|
|
49290
|
+
}
|
|
49291
|
+
/**
|
|
49292
|
+
* Returns the automatic refresh interval for the current UI state.
|
|
49293
|
+
*
|
|
49294
|
+
* Waiting, paused, and completed states return `undefined` so the rich UI stays
|
|
49295
|
+
* perfectly still until actual state changes arrive.
|
|
49296
|
+
*
|
|
49297
|
+
* @private internal utility of coder run UI
|
|
49298
|
+
*/
|
|
49299
|
+
function getCoderRunUiAutoRefreshInterval(phase, pauseState) {
|
|
49300
|
+
return isCoderRunUiAutoRefreshing(phase, pauseState) ? ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS : undefined;
|
|
49301
|
+
}
|
|
49302
|
+
|
|
48744
49303
|
/**
|
|
48745
49304
|
* Maximum number of output lines reserved for agent output in the UI.
|
|
48746
49305
|
*/
|
|
48747
49306
|
const MAX_VISIBLE_OUTPUT_LINES = 8;
|
|
49307
|
+
/**
|
|
49308
|
+
* Minimum width used for the rich coder-run frame.
|
|
49309
|
+
*/
|
|
49310
|
+
const MIN_FRAME_WIDTH = 56;
|
|
49311
|
+
/**
|
|
49312
|
+
* Maximum width used for the rich coder-run frame.
|
|
49313
|
+
*/
|
|
49314
|
+
const MAX_FRAME_WIDTH = 96;
|
|
49315
|
+
/**
|
|
49316
|
+
* Visible width reserved for aligned labels in the session box.
|
|
49317
|
+
*/
|
|
49318
|
+
const SESSION_LABEL_WIDTH = 8;
|
|
48748
49319
|
/**
|
|
48749
49320
|
* Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
|
|
48750
49321
|
*/
|
|
48751
49322
|
function buildCoderRunUiFrame(options) {
|
|
48752
|
-
const totalWidth = Math.max(
|
|
49323
|
+
const totalWidth = Math.max(MIN_FRAME_WIDTH, Math.min(options.terminalWidth, MAX_FRAME_WIDTH));
|
|
48753
49324
|
const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
|
|
48754
49325
|
const promptStatusPrefix = isPromptActive ? `${colors__default["default"].yellow(`${options.spinner} `)}` : '';
|
|
48755
|
-
const
|
|
48756
|
-
?
|
|
48757
|
-
:
|
|
48758
|
-
const
|
|
48759
|
-
const
|
|
48760
|
-
if (options.progress.skippedPrompts > 0) {
|
|
48761
|
-
sessionQueueParts.push(`Skipping ${formatPromptCount(options.progress.skippedPrompts)} with Priority <${options.config.priority}`);
|
|
48762
|
-
}
|
|
48763
|
-
if (options.progress.toBeWrittenPrompts > 0) {
|
|
48764
|
-
sessionQueueParts.push(`Write first ${formatPromptCount(options.progress.toBeWrittenPrompts)}`);
|
|
48765
|
-
}
|
|
48766
|
-
const sessionLines = [
|
|
48767
|
-
`${buildPhaseBadge(options.phase, options.pauseState)} ${fitPlainText(options.statusMessage, totalWidth - 18)}`,
|
|
48768
|
-
sessionScopeLine,
|
|
48769
|
-
sessionCountLine,
|
|
48770
|
-
...(sessionQueueParts.length > 0 ? [sessionQueueParts.join(' · ')] : []),
|
|
48771
|
-
`Elapsed ${options.progress.elapsedText} · Est. total ${options.progress.estimatedTotalText} · Est. done ${options.progress.estimatedLabel}`,
|
|
48772
|
-
buildProgressBar(options.progress.percentage, totalWidth - 6, `${options.progress.percentage}% complete (${options.progress.sessionDone}/${options.progress.sessionTotal} done)`),
|
|
48773
|
-
];
|
|
48774
|
-
const metadataParts = [options.config.agentName || 'No agent selected'];
|
|
48775
|
-
if (options.config.modelName) {
|
|
48776
|
-
metadataParts.push(options.config.modelName);
|
|
48777
|
-
}
|
|
48778
|
-
if (options.config.thinkingLevel) {
|
|
48779
|
-
metadataParts.push(`thinking ${options.config.thinkingLevel}`);
|
|
48780
|
-
}
|
|
48781
|
-
const runnerDetails = [
|
|
48782
|
-
[`${colors__default["default"].bgCyan.black(' PTBK ')}`, colors__default["default"].bgBlue.white(' CODER '), colors__default["default"].bold.white(' Promptbook Coder')]
|
|
48783
|
-
.join(''),
|
|
48784
|
-
metadataParts.join(' · '),
|
|
48785
|
-
buildConfigSummaryLine(options.config),
|
|
48786
|
-
];
|
|
49326
|
+
const octopusAnimationFrame = isCoderRunUiAutoRefreshing(options.phase, options.pauseState)
|
|
49327
|
+
? options.animationFrame
|
|
49328
|
+
: 0;
|
|
49329
|
+
const pausePresentation = buildPausePresentation(options.phase, options.pauseState, options.statusMessage);
|
|
49330
|
+
const sessionLines = buildSessionLines(options, totalWidth, pausePresentation);
|
|
48787
49331
|
const currentTaskLines = options.currentPromptLabel
|
|
48788
49332
|
? [
|
|
48789
49333
|
`${promptStatusPrefix}${colors__default["default"].bold.white(fitPlainText(options.currentPromptLabel, totalWidth - 8))}`,
|
|
@@ -48792,9 +49336,10 @@ bash "$1"
|
|
|
48792
49336
|
]
|
|
48793
49337
|
: [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
|
|
48794
49338
|
const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
|
|
48795
|
-
const controls = buildControlPills(
|
|
49339
|
+
const controls = buildControlPills(pausePresentation.pauseControl, options.pendingEnterLabel).join(' ');
|
|
48796
49340
|
const frame = [
|
|
48797
|
-
...
|
|
49341
|
+
...buildCoderRunOctopusVisual({ totalWidth, animationFrame: octopusAnimationFrame }),
|
|
49342
|
+
'',
|
|
48798
49343
|
...renderBox('Session', sessionLines, totalWidth, colors__default["default"].yellow.bold),
|
|
48799
49344
|
...renderBox(options.currentPromptLabel ? 'Current task' : 'Queue', currentTaskLines, totalWidth, colors__default["default"].magenta.bold),
|
|
48800
49345
|
...renderBox('Live output', visibleOutputLines, totalWidth, colors__default["default"].green.bold),
|
|
@@ -48805,13 +49350,67 @@ bash "$1"
|
|
|
48805
49350
|
frame.push(...renderBox('Controls', [controls], totalWidth, colors__default["default"].white.bold));
|
|
48806
49351
|
return frame;
|
|
48807
49352
|
}
|
|
49353
|
+
/**
|
|
49354
|
+
* Builds the structured session lines that combine state, runner, queue, and timing metadata.
|
|
49355
|
+
*/
|
|
49356
|
+
function buildSessionLines(options, totalWidth, pausePresentation) {
|
|
49357
|
+
const bodyWidth = Math.max(10, totalWidth - 4);
|
|
49358
|
+
return buildSessionRows(options, bodyWidth, pausePresentation).map((sessionRow) => buildLabeledSessionLine(sessionRow.label, sessionRow.value, bodyWidth));
|
|
49359
|
+
}
|
|
49360
|
+
/**
|
|
49361
|
+
* Builds the session rows so the renderer can keep one consistent structure without duplicating labels.
|
|
49362
|
+
*/
|
|
49363
|
+
function buildSessionRows(options, bodyWidth, pausePresentation) {
|
|
49364
|
+
const runnerParts = [options.config.agentName || 'No agent selected'];
|
|
49365
|
+
if (options.config.modelName) {
|
|
49366
|
+
runnerParts.push(options.config.modelName);
|
|
49367
|
+
}
|
|
49368
|
+
if (options.config.thinkingLevel) {
|
|
49369
|
+
runnerParts.push(`thinking ${options.config.thinkingLevel}`);
|
|
49370
|
+
}
|
|
49371
|
+
const configurationRows = [
|
|
49372
|
+
...buildOptionalSessionRow('Context', options.config.context),
|
|
49373
|
+
...buildOptionalSessionRow('Test', options.config.testCommand),
|
|
49374
|
+
];
|
|
49375
|
+
return [
|
|
49376
|
+
{
|
|
49377
|
+
label: 'State',
|
|
49378
|
+
value: `${pausePresentation.badge} ${pausePresentation.stateMessage}`,
|
|
49379
|
+
},
|
|
49380
|
+
{
|
|
49381
|
+
label: 'Runner',
|
|
49382
|
+
value: runnerParts.join(' · '),
|
|
49383
|
+
},
|
|
49384
|
+
...configurationRows,
|
|
49385
|
+
{
|
|
49386
|
+
label: 'This run',
|
|
49387
|
+
value: buildThisRunSummary(options.progress),
|
|
49388
|
+
},
|
|
49389
|
+
{
|
|
49390
|
+
label: 'Backlog',
|
|
49391
|
+
value: buildBacklogSummary(options.progress),
|
|
49392
|
+
},
|
|
49393
|
+
{
|
|
49394
|
+
label: 'Scope',
|
|
49395
|
+
value: buildScopeSummary(options.progress, options.config),
|
|
49396
|
+
},
|
|
49397
|
+
{
|
|
49398
|
+
label: 'Timing',
|
|
49399
|
+
value: buildTimingSummary(options.progress),
|
|
49400
|
+
},
|
|
49401
|
+
{
|
|
49402
|
+
label: 'Progress',
|
|
49403
|
+
value: buildProgressBar(options.progress.percentage, bodyWidth - SESSION_LABEL_WIDTH - 1, `${options.progress.percentage}% complete (${options.progress.sessionDone}/${options.progress.sessionTotal} done)`),
|
|
49404
|
+
},
|
|
49405
|
+
];
|
|
49406
|
+
}
|
|
48808
49407
|
/**
|
|
48809
49408
|
* Builds the fixed-height live output section so streaming updates do not keep resizing the frame.
|
|
48810
49409
|
*/
|
|
48811
49410
|
function buildVisibleOutputLines(agentOutputLines) {
|
|
48812
49411
|
const visibleOutputLines = agentOutputLines.length > 0
|
|
48813
49412
|
? agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES).map((line) => `› ${stripAnsi(line)}`)
|
|
48814
|
-
: ['No live agent output yet.'];
|
|
49413
|
+
: [colors__default["default"].gray('No live agent output yet.')];
|
|
48815
49414
|
while (visibleOutputLines.length < MAX_VISIBLE_OUTPUT_LINES) {
|
|
48816
49415
|
visibleOutputLines.push('');
|
|
48817
49416
|
}
|
|
@@ -48834,25 +49433,84 @@ bash "$1"
|
|
|
48834
49433
|
return [topBorder, ...body, bottomBorder];
|
|
48835
49434
|
}
|
|
48836
49435
|
/**
|
|
48837
|
-
* Builds
|
|
49436
|
+
* Builds one aligned labeled line inside the session box.
|
|
48838
49437
|
*/
|
|
48839
|
-
function
|
|
48840
|
-
const
|
|
48841
|
-
|
|
48842
|
-
|
|
49438
|
+
function buildLabeledSessionLine(label, value, bodyWidth) {
|
|
49439
|
+
const formattedLabel = colors__default["default"].gray(label.padEnd(SESSION_LABEL_WIDTH));
|
|
49440
|
+
return `${formattedLabel} ${fitAnsiText(value, bodyWidth - SESSION_LABEL_WIDTH - 1)}`;
|
|
49441
|
+
}
|
|
49442
|
+
/**
|
|
49443
|
+
* Builds zero or one structured session row for optional metadata.
|
|
49444
|
+
*/
|
|
49445
|
+
function buildOptionalSessionRow(label, value) {
|
|
49446
|
+
if (!value) {
|
|
49447
|
+
return [];
|
|
48843
49448
|
}
|
|
48844
|
-
|
|
48845
|
-
|
|
49449
|
+
return [{ label, value }];
|
|
49450
|
+
}
|
|
49451
|
+
/**
|
|
49452
|
+
* Builds the active-session summary shown in the session box.
|
|
49453
|
+
*/
|
|
49454
|
+
function buildThisRunSummary(progress) {
|
|
49455
|
+
if (progress.sessionTotal === 0) {
|
|
49456
|
+
return 'No runnable prompts in current scope';
|
|
49457
|
+
}
|
|
49458
|
+
return `Task ${progress.currentPromptIndex}/${progress.sessionTotal} · ${progress.sessionDone} done · ${progress.sessionRemaining} left`;
|
|
49459
|
+
}
|
|
49460
|
+
/**
|
|
49461
|
+
* Builds the backlog/filter summary shown in the session box.
|
|
49462
|
+
*/
|
|
49463
|
+
function buildBacklogSummary(progress) {
|
|
49464
|
+
const parts = [`Repo ${progress.totalPrompts} total`];
|
|
49465
|
+
if (progress.skippedPrompts > 0) {
|
|
49466
|
+
parts.push(`${formatPromptCount(progress.skippedPrompts)} below priority`);
|
|
49467
|
+
}
|
|
49468
|
+
return parts.join(' · ');
|
|
49469
|
+
}
|
|
49470
|
+
/**
|
|
49471
|
+
* Builds the priority/write-order summary shown in the session box.
|
|
49472
|
+
*/
|
|
49473
|
+
function buildScopeSummary(progress, config) {
|
|
49474
|
+
const parts = [`Priority ≥${config.priority}`];
|
|
49475
|
+
if (progress.toBeWrittenPrompts > 0) {
|
|
49476
|
+
parts.push(`Write ${formatPromptCount(progress.toBeWrittenPrompts)} first`);
|
|
48846
49477
|
}
|
|
48847
49478
|
return parts.join(' · ');
|
|
48848
49479
|
}
|
|
49480
|
+
/**
|
|
49481
|
+
* Builds the elapsed/estimate summary shown in the session box.
|
|
49482
|
+
*/
|
|
49483
|
+
function buildTimingSummary(progress) {
|
|
49484
|
+
return `Elapsed ${progress.elapsedText} · Total ${progress.estimatedTotalText} · ETA ${progress.estimatedLabel}`;
|
|
49485
|
+
}
|
|
48849
49486
|
/**
|
|
48850
49487
|
* Builds the colored phase badge shown in the session box.
|
|
48851
49488
|
*/
|
|
48852
|
-
function
|
|
48853
|
-
if (pauseState
|
|
48854
|
-
return
|
|
49489
|
+
function buildPausePresentation(phase, pauseState, statusMessage) {
|
|
49490
|
+
if (pauseState === 'PAUSING') {
|
|
49491
|
+
return {
|
|
49492
|
+
badge: colors__default["default"].bgYellow.black(' PAUSING '),
|
|
49493
|
+
stateMessage: 'Pausing before the next task',
|
|
49494
|
+
pauseControl: colors__default["default"].bgMagenta.white(' P ') + colors__default["default"].white(' Cancel pause'),
|
|
49495
|
+
};
|
|
49496
|
+
}
|
|
49497
|
+
if (pauseState === 'PAUSED') {
|
|
49498
|
+
return {
|
|
49499
|
+
badge: colors__default["default"].bgWhite.black(' PAUSED '),
|
|
49500
|
+
stateMessage: 'Paused until resumed',
|
|
49501
|
+
pauseControl: colors__default["default"].bgGreen.black(' P ') + colors__default["default"].white(' Resume'),
|
|
49502
|
+
};
|
|
48855
49503
|
}
|
|
49504
|
+
return {
|
|
49505
|
+
badge: buildRunningPhaseBadge(phase),
|
|
49506
|
+
stateMessage: statusMessage,
|
|
49507
|
+
pauseControl: colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Pause'),
|
|
49508
|
+
};
|
|
49509
|
+
}
|
|
49510
|
+
/**
|
|
49511
|
+
* Builds the active phase badge shown in the session box while the runner is not paused.
|
|
49512
|
+
*/
|
|
49513
|
+
function buildRunningPhaseBadge(phase) {
|
|
48856
49514
|
switch (phase) {
|
|
48857
49515
|
case 'loading':
|
|
48858
49516
|
case 'initializing':
|
|
@@ -48867,6 +49525,8 @@ bash "$1"
|
|
|
48867
49525
|
return colors__default["default"].bgGreen.black(' DONE ');
|
|
48868
49526
|
case 'error':
|
|
48869
49527
|
return colors__default["default"].bgRed.white(' ERROR ');
|
|
49528
|
+
case 'paused':
|
|
49529
|
+
return colors__default["default"].bgWhite.black(' READY ');
|
|
48870
49530
|
default:
|
|
48871
49531
|
return colors__default["default"].bgWhite.black(' READY ');
|
|
48872
49532
|
}
|
|
@@ -48879,7 +49539,7 @@ bash "$1"
|
|
|
48879
49539
|
const barWidth = Math.max(10, availableWidth - percentageLabel.length - 1);
|
|
48880
49540
|
const filledWidth = Math.round((percentage / 100) * barWidth);
|
|
48881
49541
|
const emptyWidth = Math.max(0, barWidth - filledWidth);
|
|
48882
|
-
return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].
|
|
49542
|
+
return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].blue('░'.repeat(emptyWidth))} ${percentageLabel}`;
|
|
48883
49543
|
}
|
|
48884
49544
|
/**
|
|
48885
49545
|
* Formats a prompt count with singular/plural wording.
|
|
@@ -48890,58 +49550,15 @@ bash "$1"
|
|
|
48890
49550
|
/**
|
|
48891
49551
|
* Builds the control pills shown in the footer box.
|
|
48892
49552
|
*/
|
|
48893
|
-
function buildControlPills(
|
|
49553
|
+
function buildControlPills(pauseControl, pendingEnterLabel) {
|
|
48894
49554
|
const pills = [];
|
|
48895
49555
|
if (pendingEnterLabel) {
|
|
48896
49556
|
pills.push(colors__default["default"].bgWhite.black(' ENTER ') + colors__default["default"].white(` ${pendingEnterLabel}`));
|
|
48897
49557
|
}
|
|
48898
|
-
pills.push(
|
|
48899
|
-
? colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Pause')
|
|
48900
|
-
: colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Resume'));
|
|
49558
|
+
pills.push(pauseControl);
|
|
48901
49559
|
pills.push(colors__default["default"].bgRed.white(' CTRL+C ') + colors__default["default"].white(' Exit'));
|
|
48902
49560
|
return pills;
|
|
48903
49561
|
}
|
|
48904
|
-
/**
|
|
48905
|
-
* Pads or truncates a possibly ANSI-colored line to the target visible width.
|
|
48906
|
-
*/
|
|
48907
|
-
function padAnsiText(text, width) {
|
|
48908
|
-
const fittedText = fitAnsiText(text, width);
|
|
48909
|
-
return fittedText + ' '.repeat(Math.max(0, width - visibleLength(fittedText)));
|
|
48910
|
-
}
|
|
48911
|
-
/**
|
|
48912
|
-
* Truncates a possibly ANSI-colored line to the target visible width.
|
|
48913
|
-
*/
|
|
48914
|
-
function fitAnsiText(text, width) {
|
|
48915
|
-
if (visibleLength(text) <= width) {
|
|
48916
|
-
return text;
|
|
48917
|
-
}
|
|
48918
|
-
return fitPlainText(stripAnsi(text), width);
|
|
48919
|
-
}
|
|
48920
|
-
/**
|
|
48921
|
-
* Truncates a plain-text line to the target width with an ellipsis.
|
|
48922
|
-
*/
|
|
48923
|
-
function fitPlainText(text, width) {
|
|
48924
|
-
if (text.length <= width) {
|
|
48925
|
-
return text;
|
|
48926
|
-
}
|
|
48927
|
-
if (width <= 3) {
|
|
48928
|
-
return '.'.repeat(width);
|
|
48929
|
-
}
|
|
48930
|
-
return `${text.slice(0, width - 3)}...`;
|
|
48931
|
-
}
|
|
48932
|
-
/**
|
|
48933
|
-
* Measures visible string width by stripping ANSI escape codes.
|
|
48934
|
-
*/
|
|
48935
|
-
function visibleLength(text) {
|
|
48936
|
-
return stripAnsi(text).length;
|
|
48937
|
-
}
|
|
48938
|
-
/**
|
|
48939
|
-
* Strips ANSI escape codes from a string.
|
|
48940
|
-
*/
|
|
48941
|
-
function stripAnsi(text) {
|
|
48942
|
-
// eslint-disable-next-line no-control-regex
|
|
48943
|
-
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
48944
|
-
}
|
|
48945
49562
|
|
|
48946
49563
|
/**
|
|
48947
49564
|
* Maximum number of agent output lines kept in the scrolling output area.
|
|
@@ -48970,7 +49587,9 @@ bash "$1"
|
|
|
48970
49587
|
this.statusMessage = 'Initializing...';
|
|
48971
49588
|
this.errors = [];
|
|
48972
49589
|
this.stats = { done: 0, forAgent: 0, belowMinimumPriority: 0, toBeWritten: 0 };
|
|
48973
|
-
|
|
49590
|
+
// Match the plain CLI progress header and count active run time from startup.
|
|
49591
|
+
// Explicit waits/pauses are excluded later through `pauseTimer()` / `resumeTimer()`.
|
|
49592
|
+
this.timer = new CoderRunTimer(startTime);
|
|
48974
49593
|
}
|
|
48975
49594
|
/**
|
|
48976
49595
|
* Pauses the elapsed timer (e.g. while waiting for user input or paused state).
|
|
@@ -49082,34 +49701,6 @@ bash "$1"
|
|
|
49082
49701
|
}
|
|
49083
49702
|
}
|
|
49084
49703
|
|
|
49085
|
-
/**
|
|
49086
|
-
* Refresh cadence used only while the rich coder UI needs animated updates.
|
|
49087
|
-
*
|
|
49088
|
-
* @private internal constant of coder run UI
|
|
49089
|
-
*/
|
|
49090
|
-
const ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS = 1000;
|
|
49091
|
-
/**
|
|
49092
|
-
* Phases that still benefit from automatic refreshes because the frame can change
|
|
49093
|
-
* over time even without new runner output.
|
|
49094
|
-
*
|
|
49095
|
-
* @private internal constant of coder run UI
|
|
49096
|
-
*/
|
|
49097
|
-
const AUTO_REFRESH_PHASES = ['initializing', 'loading', 'running', 'verifying'];
|
|
49098
|
-
/**
|
|
49099
|
-
* Returns the automatic refresh interval for the current UI state.
|
|
49100
|
-
*
|
|
49101
|
-
* Waiting, paused, and completed states return `undefined` so the rich UI stays
|
|
49102
|
-
* perfectly still until actual state changes arrive.
|
|
49103
|
-
*
|
|
49104
|
-
* @private internal utility of coder run UI
|
|
49105
|
-
*/
|
|
49106
|
-
function getCoderRunUiAutoRefreshInterval(phase, pauseState) {
|
|
49107
|
-
if (pauseState !== 'RUNNING') {
|
|
49108
|
-
return undefined;
|
|
49109
|
-
}
|
|
49110
|
-
return AUTO_REFRESH_PHASES.includes(phase) ? ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS : undefined;
|
|
49111
|
-
}
|
|
49112
|
-
|
|
49113
49704
|
/**
|
|
49114
49705
|
* Spinner animation frames.
|
|
49115
49706
|
*
|
|
@@ -49288,6 +49879,7 @@ bash "$1"
|
|
|
49288
49879
|
function buildFrameLines() {
|
|
49289
49880
|
return buildCoderRunUiFrame({
|
|
49290
49881
|
terminalWidth: getTerminalWidth(),
|
|
49882
|
+
animationFrame: spinnerFrame,
|
|
49291
49883
|
spinner: SPINNER_FRAMES[spinnerFrame],
|
|
49292
49884
|
pauseState: getPauseState(),
|
|
49293
49885
|
config: state.config,
|
|
@@ -49335,12 +49927,7 @@ bash "$1"
|
|
|
49335
49927
|
process.exit(0);
|
|
49336
49928
|
}
|
|
49337
49929
|
if (key.name === 'p') {
|
|
49338
|
-
|
|
49339
|
-
requestPause();
|
|
49340
|
-
}
|
|
49341
|
-
else {
|
|
49342
|
-
requestResume();
|
|
49343
|
-
}
|
|
49930
|
+
togglePauseState();
|
|
49344
49931
|
scheduleRender();
|
|
49345
49932
|
return;
|
|
49346
49933
|
}
|
|
@@ -49476,6 +50063,23 @@ bash "$1"
|
|
|
49476
50063
|
const isRichUiEnabled = !options.dryRun && !options.noUi && Boolean(process.stdout.isTTY);
|
|
49477
50064
|
const progressDisplay = options.dryRun || options.noUi || isRichUiEnabled ? undefined : new CliProgressDisplay(runStartDate, options.priority);
|
|
49478
50065
|
const uiHandle = isRichUiEnabled ? renderCoderRunUi(runStartDate) : undefined;
|
|
50066
|
+
const waitForRequestedPause = async () => {
|
|
50067
|
+
await checkPause({
|
|
50068
|
+
silent: isRichUiEnabled,
|
|
50069
|
+
onPaused: () => {
|
|
50070
|
+
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
|
|
50071
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
50072
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
|
|
50073
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
|
|
50074
|
+
},
|
|
50075
|
+
onResumed: () => {
|
|
50076
|
+
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
|
|
50077
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
50078
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
50079
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Resuming...');
|
|
50080
|
+
},
|
|
50081
|
+
});
|
|
50082
|
+
};
|
|
49479
50083
|
// When the Ink UI is active it handles keyboard input itself, so skip the raw stdin listener.
|
|
49480
50084
|
if (!isRichUiEnabled) {
|
|
49481
50085
|
listenForPause();
|
|
@@ -49592,19 +50196,7 @@ bash "$1"
|
|
|
49592
50196
|
let hasShownUpcomingTasks = false;
|
|
49593
50197
|
let hasWaitedForStart = false;
|
|
49594
50198
|
while (just(true)) {
|
|
49595
|
-
await
|
|
49596
|
-
silent: isRichUiEnabled,
|
|
49597
|
-
onPaused: () => {
|
|
49598
|
-
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
|
|
49599
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49600
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
|
|
49601
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
|
|
49602
|
-
},
|
|
49603
|
-
onResumed: () => {
|
|
49604
|
-
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
|
|
49605
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
49606
|
-
},
|
|
49607
|
-
});
|
|
50199
|
+
await waitForRequestedPause();
|
|
49608
50200
|
if (isRichUiEnabled) {
|
|
49609
50201
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
49610
50202
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Loading prompts...');
|
|
@@ -49672,6 +50264,7 @@ bash "$1"
|
|
|
49672
50264
|
const commitMessage = buildCommitMessage(nextPrompt.file, nextPrompt.section);
|
|
49673
50265
|
const codexPrompt = appendCoderContext(buildCodexPrompt(nextPrompt.file, nextPrompt.section), resolvedCoderContext);
|
|
49674
50266
|
const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
|
|
50267
|
+
await waitForRequestedPause();
|
|
49675
50268
|
if (isRichUiEnabled) {
|
|
49676
50269
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
|
|
49677
50270
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
|
|
@@ -51382,6 +51975,14 @@ bash "$1"
|
|
|
51382
51975
|
* Constant for default agent kit model name.
|
|
51383
51976
|
*/
|
|
51384
51977
|
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-mini';
|
|
51978
|
+
/**
|
|
51979
|
+
* Default model used for nested DeepSearch tool invocations.
|
|
51980
|
+
*/
|
|
51981
|
+
const DEFAULT_DEEP_SEARCH_MODEL_NAME = 'o4-mini-deep-research';
|
|
51982
|
+
/**
|
|
51983
|
+
* Tool name used by the Book commitment-backed DeepSearch capability.
|
|
51984
|
+
*/
|
|
51985
|
+
const DEEP_SEARCH_TOOL_NAME = 'deep_search';
|
|
51385
51986
|
/**
|
|
51386
51987
|
* Creates one structured log entry for streamed tool-call updates.
|
|
51387
51988
|
*
|
|
@@ -51424,6 +52025,98 @@ bash "$1"
|
|
|
51424
52025
|
}
|
|
51425
52026
|
return 'COMPLETE';
|
|
51426
52027
|
}
|
|
52028
|
+
/**
|
|
52029
|
+
* Returns true when one tool definition represents the dedicated DeepSearch capability.
|
|
52030
|
+
*
|
|
52031
|
+
* @param toolDefinition - Tool definition from compiled model requirements.
|
|
52032
|
+
* @returns `true` when the tool should be backed by a nested deep-research agent.
|
|
52033
|
+
*
|
|
52034
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
52035
|
+
*/
|
|
52036
|
+
function isDeepSearchToolDefinition(toolDefinition) {
|
|
52037
|
+
return toolDefinition.name === DEEP_SEARCH_TOOL_NAME;
|
|
52038
|
+
}
|
|
52039
|
+
/**
|
|
52040
|
+
* Normalizes Promptbook JSON-schema tool parameters for AgentKit function tools.
|
|
52041
|
+
*
|
|
52042
|
+
* @param parameters - Promptbook tool parameters.
|
|
52043
|
+
* @returns AgentKit-compatible JSON schema or `undefined`.
|
|
52044
|
+
*
|
|
52045
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
52046
|
+
*/
|
|
52047
|
+
function normalizeAgentKitToolParameters(parameters) {
|
|
52048
|
+
var _a, _b;
|
|
52049
|
+
if (!parameters) {
|
|
52050
|
+
return undefined;
|
|
52051
|
+
}
|
|
52052
|
+
return {
|
|
52053
|
+
...parameters,
|
|
52054
|
+
additionalProperties: (_a = parameters.additionalProperties) !== null && _a !== void 0 ? _a : false,
|
|
52055
|
+
required: (_b = parameters.required) !== null && _b !== void 0 ? _b : [],
|
|
52056
|
+
};
|
|
52057
|
+
}
|
|
52058
|
+
/**
|
|
52059
|
+
* Creates instructions for the nested DeepSearch specialist agent.
|
|
52060
|
+
*
|
|
52061
|
+
* @param toolDescription - Model-facing description from the original tool definition.
|
|
52062
|
+
* @returns System instructions for the nested deep-research agent.
|
|
52063
|
+
*
|
|
52064
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
52065
|
+
*/
|
|
52066
|
+
function createDeepSearchAgentInstructions(toolDescription) {
|
|
52067
|
+
const normalizedDescription = toolDescription.trim();
|
|
52068
|
+
return _spaceTrim.spaceTrim((block) => `
|
|
52069
|
+
You are a DeepSearch specialist working as a tool for another agent.
|
|
52070
|
+
Perform thorough, source-grounded public-web research based on the provided request.
|
|
52071
|
+
Use web search to gather current information, compare relevant viewpoints, and synthesize a concise research brief.
|
|
52072
|
+
Do not ask follow-up questions. If the request is not specific enough, state the assumptions you had to make.
|
|
52073
|
+
Include citations in the research brief whenever sources were used.
|
|
52074
|
+
${block(normalizedDescription ? `Tool guidance:\n${normalizedDescription}` : '')}
|
|
52075
|
+
`);
|
|
52076
|
+
}
|
|
52077
|
+
/**
|
|
52078
|
+
* Builds the nested DeepSearch prompt from structured tool arguments.
|
|
52079
|
+
*
|
|
52080
|
+
* @param rawInput - Parsed function-tool arguments provided by the outer agent.
|
|
52081
|
+
* @returns Prompt text passed to the nested deep-research agent.
|
|
52082
|
+
*
|
|
52083
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
52084
|
+
*/
|
|
52085
|
+
function buildDeepSearchToolInput(rawInput) {
|
|
52086
|
+
const input = rawInput && typeof rawInput === 'object' ? rawInput : {};
|
|
52087
|
+
const query = typeof input.query === 'string' ? input.query.trim() : '';
|
|
52088
|
+
const additionalHints = Object.entries(input)
|
|
52089
|
+
.filter(([key, value]) => key !== 'query' && value !== undefined && value !== null && String(value).trim() !== '')
|
|
52090
|
+
.map(([key, value]) => `- ${key}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
|
|
52091
|
+
return _spaceTrim.spaceTrim((block) => `
|
|
52092
|
+
Research request:
|
|
52093
|
+
${query || JSON.stringify(input)}
|
|
52094
|
+
${block(additionalHints.length > 0 ? `Execution hints:\n${additionalHints.join('\n')}` : '')}
|
|
52095
|
+
`);
|
|
52096
|
+
}
|
|
52097
|
+
/**
|
|
52098
|
+
* Creates the native Agent SDK tool used for `USE DEEPSEARCH`.
|
|
52099
|
+
*
|
|
52100
|
+
* @param toolDefinition - Promptbook tool definition for `deep_search`.
|
|
52101
|
+
* @returns AgentKit tool backed by a nested deep-research agent.
|
|
52102
|
+
*
|
|
52103
|
+
* @private helper of `OpenAiAgentKitExecutionTools`
|
|
52104
|
+
*/
|
|
52105
|
+
function createDeepSearchAgentKitTool(toolDefinition) {
|
|
52106
|
+
const deepSearchAgent = new agents.Agent({
|
|
52107
|
+
name: 'DeepSearch',
|
|
52108
|
+
model: DEFAULT_DEEP_SEARCH_MODEL_NAME,
|
|
52109
|
+
instructions: createDeepSearchAgentInstructions(toolDefinition.description),
|
|
52110
|
+
tools: [agents.webSearchTool({ searchContextSize: 'high' })],
|
|
52111
|
+
});
|
|
52112
|
+
return deepSearchAgent.asTool({
|
|
52113
|
+
toolName: toolDefinition.name,
|
|
52114
|
+
toolDescription: toolDefinition.description,
|
|
52115
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
52116
|
+
inputBuilder: ({ params }) => buildDeepSearchToolInput(params),
|
|
52117
|
+
customOutputExtractor: (result) => { var _a; return typeof result.finalOutput === 'string' ? result.finalOutput : JSON.stringify((_a = result.finalOutput) !== null && _a !== void 0 ? _a : ''); },
|
|
52118
|
+
});
|
|
52119
|
+
}
|
|
51427
52120
|
/**
|
|
51428
52121
|
* Constant for default JSON schema name.
|
|
51429
52122
|
*/
|
|
@@ -51764,25 +52457,23 @@ bash "$1"
|
|
|
51764
52457
|
* Builds the tool list for AgentKit, including hosted file search when applicable.
|
|
51765
52458
|
*/
|
|
51766
52459
|
buildAgentKitTools(options) {
|
|
51767
|
-
var _a;
|
|
51768
52460
|
const { tools, vectorStoreId } = options;
|
|
51769
52461
|
const agentKitTools = [];
|
|
51770
52462
|
if (vectorStoreId) {
|
|
51771
52463
|
agentKitTools.push(agents.fileSearchTool(vectorStoreId));
|
|
51772
52464
|
}
|
|
51773
52465
|
if (tools && tools.length > 0) {
|
|
51774
|
-
|
|
52466
|
+
let scriptTools = null;
|
|
51775
52467
|
for (const toolDefinition of tools) {
|
|
52468
|
+
if (isDeepSearchToolDefinition(toolDefinition)) {
|
|
52469
|
+
agentKitTools.push(createDeepSearchAgentKitTool(toolDefinition));
|
|
52470
|
+
continue;
|
|
52471
|
+
}
|
|
52472
|
+
scriptTools !== null && scriptTools !== void 0 ? scriptTools : (scriptTools = this.resolveScriptTools());
|
|
51776
52473
|
agentKitTools.push(agents.tool({
|
|
51777
52474
|
name: toolDefinition.name,
|
|
51778
52475
|
description: toolDefinition.description,
|
|
51779
|
-
parameters: toolDefinition.parameters
|
|
51780
|
-
? {
|
|
51781
|
-
...toolDefinition.parameters,
|
|
51782
|
-
additionalProperties: false,
|
|
51783
|
-
required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
|
|
51784
|
-
}
|
|
51785
|
-
: undefined,
|
|
52476
|
+
parameters: normalizeAgentKitToolParameters(toolDefinition.parameters),
|
|
51786
52477
|
strict: false,
|
|
51787
52478
|
execute: async (input, runContext, details) => {
|
|
51788
52479
|
var _a, _b, _c, _d;
|
|
@@ -53736,9 +54427,10 @@ bash "$1"
|
|
|
53736
54427
|
* Build a minimal agent source snapshot for remote agents.
|
|
53737
54428
|
*/
|
|
53738
54429
|
function buildRemoteAgentSource(profile, meta) {
|
|
54430
|
+
const isMetaImageExplicit = profile.isMetaImageExplicit !== false;
|
|
53739
54431
|
const metaLines = [
|
|
53740
54432
|
formatMetaLine('FULLNAME', meta === null || meta === void 0 ? void 0 : meta.fullname),
|
|
53741
|
-
formatMetaLine('IMAGE', meta === null || meta === void 0 ? void 0 : meta.image),
|
|
54433
|
+
formatMetaLine('IMAGE', isMetaImageExplicit ? meta === null || meta === void 0 ? void 0 : meta.image : undefined),
|
|
53742
54434
|
formatMetaLine('DESCRIPTION', meta === null || meta === void 0 ? void 0 : meta.description),
|
|
53743
54435
|
formatMetaLine('COLOR', meta === null || meta === void 0 ? void 0 : meta.color),
|
|
53744
54436
|
formatMetaLine('FONT', meta === null || meta === void 0 ? void 0 : meta.font),
|
|
@@ -53837,6 +54529,8 @@ bash "$1"
|
|
|
53837
54529
|
remoteAgent._isVoiceCallingEnabled = profile.isVoiceCallingEnabled === true; // [✨✷] Store voice calling status
|
|
53838
54530
|
remoteAgent._isVoiceTtsSttEnabled = profile.isVoiceTtsSttEnabled !== false;
|
|
53839
54531
|
remoteAgent.knowledgeSources = profile.knowledgeSources || [];
|
|
54532
|
+
remoteAgent.isMetaImageExplicit = profile.isMetaImageExplicit !== false;
|
|
54533
|
+
remoteAgent.avatarVisualId = profile.avatarVisualId;
|
|
53840
54534
|
return remoteAgent;
|
|
53841
54535
|
}
|
|
53842
54536
|
/**
|
|
@@ -53852,6 +54546,8 @@ bash "$1"
|
|
|
53852
54546
|
this.toolTitles = {};
|
|
53853
54547
|
this._isVoiceCallingEnabled = false; // [✨✷] Track voice calling status
|
|
53854
54548
|
this._isVoiceTtsSttEnabled = true;
|
|
54549
|
+
this.isMetaImageExplicit = true;
|
|
54550
|
+
this.avatarVisualId = undefined;
|
|
53855
54551
|
this.knowledgeSources = [];
|
|
53856
54552
|
this.agentUrl = options.agentUrl;
|
|
53857
54553
|
}
|