@promptbook/cli 0.112.0-42 → 0.112.0-44
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 +10 -3
- package/esm/index.es.js +1485 -171
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScript.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScriptUntilMarkerIdle.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/runGoScript/$runGoScriptWithOutput.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/runGoScript/PromptRoundArtifacts.d.ts +35 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/buildScriptLogPath.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/runBashScriptWithOutput.d.ts +5 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +28 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/withPromptRuntimeLog.d.ts +5 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/withTempScript.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/common/waitForPause.d.ts +21 -1
- package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +2 -2
- package/esm/scripts/run-codex-prompts/prompts/formatPromptAttemptMetadata.d.ts +4 -0
- package/esm/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/prompts/markPromptFailed.d.ts +1 -1
- package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +14 -0
- package/esm/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +25 -0
- package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +112 -0
- package/esm/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +30 -0
- package/esm/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -1
- package/esm/src/book-2.0/agent-source/TeammateProfileResolver.d.ts +2 -1
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/getTypescriptModule.d.ts +19 -0
- package/esm/src/cli/cli-commands/coder/getTypescriptModule.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/coder/mergeStringRecordJsonFile.test.d.ts +1 -0
- package/esm/src/commitments/PERSONA/PERSONA.d.ts +7 -0
- package/esm/src/llm-providers/agent/Agent.test.d.ts +1 -0
- package/esm/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +4 -0
- package/esm/src/llm-providers/agent/AgentOptions.d.ts +8 -0
- package/esm/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +9 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +3 -2
- package/umd/index.umd.js +1487 -174
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/formatUnknownErrorDetails.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScript.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScriptUntilMarkerIdle.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/runGoScript/$runGoScriptWithOutput.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/runGoScript/PromptRoundArtifacts.d.ts +35 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/buildScriptLogPath.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/runBashScriptWithOutput.d.ts +5 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +28 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/withPromptRuntimeLog.d.ts +5 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/withTempScript.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/common/waitForPause.d.ts +21 -1
- package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +2 -2
- package/umd/scripts/run-codex-prompts/prompts/formatPromptAttemptMetadata.d.ts +4 -0
- package/umd/scripts/run-codex-prompts/prompts/markPromptDone.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/prompts/markPromptFailed.d.ts +1 -1
- package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +14 -0
- package/umd/scripts/run-codex-prompts/testing/runPromptWithTestFeedback.d.ts +25 -0
- package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +112 -0
- package/umd/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +30 -0
- package/umd/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -1
- package/umd/src/book-2.0/agent-source/TeammateProfileResolver.d.ts +2 -1
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/getTypescriptModule.d.ts +19 -0
- package/umd/src/cli/cli-commands/coder/getTypescriptModule.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/coder/mergeStringRecordJsonFile.test.d.ts +1 -0
- package/umd/src/commitments/PERSONA/PERSONA.d.ts +7 -0
- package/umd/src/llm-providers/agent/Agent.test.d.ts +1 -0
- package/umd/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +4 -0
- package/umd/src/llm-providers/agent/AgentOptions.d.ts +8 -0
- package/umd/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +9 -0
- package/umd/src/version.d.ts +1 -1
package/esm/index.es.js
CHANGED
|
@@ -4,7 +4,7 @@ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs';
|
|
6
6
|
import { join, basename, dirname, isAbsolute, relative, extname, resolve } from 'path';
|
|
7
|
-
import { readFile, writeFile, stat, mkdir, access, constants, readdir, watch, unlink, rm, rename, rmdir } from 'fs/promises';
|
|
7
|
+
import { readFile, writeFile, stat, mkdir, access, constants, readdir, watch, unlink, rm, rename, rmdir, appendFile } from 'fs/promises';
|
|
8
8
|
import { forTime, forEver } from 'waitasecond';
|
|
9
9
|
import prompts from 'prompts';
|
|
10
10
|
import * as dotenv from 'dotenv';
|
|
@@ -37,6 +37,7 @@ import { spawn } from 'child_process';
|
|
|
37
37
|
import { Client } from 'pg';
|
|
38
38
|
import '@supabase/supabase-js';
|
|
39
39
|
import { dirname as dirname$1 } from 'path/posix';
|
|
40
|
+
import { EventEmitter } from 'events';
|
|
40
41
|
import { Subject, BehaviorSubject } from 'rxjs';
|
|
41
42
|
import { lookup, extension } from 'mime-types';
|
|
42
43
|
import { parse, unparse } from 'papaparse';
|
|
@@ -57,7 +58,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
57
58
|
* @generated
|
|
58
59
|
* @see https://github.com/webgptorg/promptbook
|
|
59
60
|
*/
|
|
60
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
61
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-44';
|
|
61
62
|
/**
|
|
62
63
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
63
64
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -2320,7 +2321,7 @@ function getDefaultCoderAgentCodingFileContent({ packageJsonScripts, }) {
|
|
|
2320
2321
|
'',
|
|
2321
2322
|
'## Customizing the workflow',
|
|
2322
2323
|
'- Edit `package.json` if you want `npm run coder:run` to use another coding agent, model, thinking level, context file, or wait mode.',
|
|
2323
|
-
'- Use direct CLI commands when you need one-off flags such as `--priority`, `--ignore-git-changes`, `--dry-run`, `--allow-credits`, or `--auto-migrate`.',
|
|
2324
|
+
'- Use direct CLI commands when you need one-off flags such as `--priority`, `--ignore-git-changes`, `--dry-run`, `--test`, `--allow-credits`, or `--auto-migrate`.',
|
|
2324
2325
|
'- Use `npx ptbk coder --help` and `npx ptbk coder <command> --help` for the full CLI reference.',
|
|
2325
2326
|
].join('\n');
|
|
2326
2327
|
}
|
|
@@ -2580,6 +2581,26 @@ class ParseError extends Error {
|
|
|
2580
2581
|
}
|
|
2581
2582
|
// TODO: Maybe split `ParseError` and `ApplyError`
|
|
2582
2583
|
|
|
2584
|
+
/**
|
|
2585
|
+
* Loads the TypeScript runtime used for parsing JSONC-style project files.
|
|
2586
|
+
*
|
|
2587
|
+
* @private internal utility of `coder init`
|
|
2588
|
+
*/
|
|
2589
|
+
async function getTypescriptModule() {
|
|
2590
|
+
return normalizeImportedTypescriptModule((await import('typescript')));
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* Normalizes CommonJS-via-`default` and direct namespace imports of TypeScript.
|
|
2594
|
+
*
|
|
2595
|
+
* @private internal utility of `getTypescriptModule`
|
|
2596
|
+
*/
|
|
2597
|
+
function normalizeImportedTypescriptModule(importedTypescriptModule) {
|
|
2598
|
+
return 'parseConfigFileTextToJson' in importedTypescriptModule
|
|
2599
|
+
? importedTypescriptModule
|
|
2600
|
+
: importedTypescriptModule.default;
|
|
2601
|
+
}
|
|
2602
|
+
// Note: [🟡] Code for coder init TypeScript loading [getTypescriptModule](src/cli/cli-commands/coder/getTypescriptModule.ts) should never be published outside of `@promptbook/cli`
|
|
2603
|
+
|
|
2583
2604
|
/**
|
|
2584
2605
|
* Default indentation used when creating new JSON configuration files.
|
|
2585
2606
|
*/
|
|
@@ -2625,7 +2646,7 @@ async function parseJsonObjectFile(relativeFilePath, fileContent) {
|
|
|
2625
2646
|
if (fileContent.trim() === '') {
|
|
2626
2647
|
return {};
|
|
2627
2648
|
}
|
|
2628
|
-
const typescript = await
|
|
2649
|
+
const typescript = await getTypescriptModule();
|
|
2629
2650
|
const parsedFile = typescript.parseConfigFileTextToJson(relativeFilePath, fileContent);
|
|
2630
2651
|
if (parsedFile.error) {
|
|
2631
2652
|
throw new ParseError(spaceTrim$1(`
|
|
@@ -2983,7 +3004,9 @@ function $initializeCoderRunCommand(program) {
|
|
|
2983
3004
|
|
|
2984
3005
|
Features:
|
|
2985
3006
|
- Automatically stages and commits changes with agent identity
|
|
3007
|
+
- Optional post-commit git push with explicit --auto-push opt-in
|
|
2986
3008
|
- Supports GPG signing of commits
|
|
3009
|
+
- Optional post-prompt verification with test-feedback retries
|
|
2987
3010
|
- Progress tracking and interactive controls
|
|
2988
3011
|
- Dry-run mode to preview prompts
|
|
2989
3012
|
`));
|
|
@@ -2996,17 +3019,20 @@ function $initializeCoderRunCommand(program) {
|
|
|
2996
3019
|
Gemini examples: gemini-3-flash-preview, default
|
|
2997
3020
|
`));
|
|
2998
3021
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
3022
|
+
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
2999
3023
|
command.addOption(new Option('--thinking-level <thinking-level>', `Set reasoning effort for supported runners (${THINKING_LEVEL_VALUES.join(', ')})`).choices([...THINKING_LEVEL_VALUES]));
|
|
3000
3024
|
command.option('--priority <minimum-priority>', 'Filter prompts by minimum priority level', parseIntOption, 0);
|
|
3001
3025
|
command.option('--no-wait', 'Skip user prompts between processing');
|
|
3002
3026
|
command.option('--ignore-git-changes', 'Skip clean working tree check before running prompts', false);
|
|
3003
3027
|
command.option('--allow-credits', 'Allow OpenAI Codex runner to spend credits when rate limits are exhausted', false);
|
|
3028
|
+
command.option('--preserve-logs', 'Keep generated runner shells and runtime logs after successful prompt rounds; failures keep them automatically', false);
|
|
3004
3029
|
command.option('--no-normalize-line-endings', 'Disable automatic LF normalization for files changed in each coding round');
|
|
3005
|
-
command.option('--
|
|
3030
|
+
command.option('--auto-push', 'Automatically git push after each commit', false);
|
|
3006
3031
|
command.option('--auto-migrate', 'Run testing-server database migrations automatically after each successfully processed prompt');
|
|
3007
3032
|
command.option('--allow-destructive-auto-migrate', 'Allow auto-migrate even when heuristic SQL safety check flags destructive pending migrations');
|
|
3008
3033
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
3009
|
-
const { dryRun, agent, model, context, thinkingLevel, priority, wait, ignoreGitChanges, allowCredits, normalizeLineEndings, autoMigrate, allowDestructiveAutoMigrate,
|
|
3034
|
+
const { dryRun, agent, model, context, test, thinkingLevel, priority, wait, ignoreGitChanges, allowCredits, preserveLogs, normalizeLineEndings, autoMigrate, allowDestructiveAutoMigrate, autoPush, } = cliOptions;
|
|
3035
|
+
const testCommand = normalizeCommandOptionValue(test);
|
|
3010
3036
|
// Validate agent
|
|
3011
3037
|
let agentName = undefined;
|
|
3012
3038
|
if (agent) {
|
|
@@ -3035,13 +3061,15 @@ function $initializeCoderRunCommand(program) {
|
|
|
3035
3061
|
agentName,
|
|
3036
3062
|
model,
|
|
3037
3063
|
context,
|
|
3064
|
+
testCommand,
|
|
3038
3065
|
thinkingLevel,
|
|
3039
3066
|
priority,
|
|
3040
3067
|
normalizeLineEndings,
|
|
3041
3068
|
allowCredits,
|
|
3069
|
+
preserveLogs,
|
|
3042
3070
|
autoMigrate,
|
|
3043
3071
|
allowDestructiveAutoMigrate,
|
|
3044
|
-
|
|
3072
|
+
autoPush,
|
|
3045
3073
|
};
|
|
3046
3074
|
// Note: Import the function dynamically to avoid loading heavy dependencies until needed
|
|
3047
3075
|
const { runCodexPrompts } = await Promise.resolve().then(function () { return runCodexPrompts$1; });
|
|
@@ -3070,6 +3098,19 @@ function parseIntOption(value) {
|
|
|
3070
3098
|
}
|
|
3071
3099
|
return parsed;
|
|
3072
3100
|
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Joins one Commander option that may be parsed either as a single string or a variadic token array.
|
|
3103
|
+
*
|
|
3104
|
+
* @private internal utility of `coder run` command
|
|
3105
|
+
*/
|
|
3106
|
+
function normalizeCommandOptionValue(value) {
|
|
3107
|
+
if (value === undefined) {
|
|
3108
|
+
return undefined;
|
|
3109
|
+
}
|
|
3110
|
+
const parts = Array.isArray(value) ? value : [value];
|
|
3111
|
+
const normalizedValue = parts.map((part) => part.trim()).filter(Boolean).join(' ').trim();
|
|
3112
|
+
return normalizedValue === '' ? undefined : normalizedValue;
|
|
3113
|
+
}
|
|
3073
3114
|
// Note: [🟡] Code for CLI command [run](src/cli/cli-commands/coder/run.ts) should never be published outside of `@promptbook/cli`
|
|
3074
3115
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
3075
3116
|
|
|
@@ -18910,7 +18951,7 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
18910
18951
|
* Short one-line description of GOAL.
|
|
18911
18952
|
*/
|
|
18912
18953
|
get description() {
|
|
18913
|
-
return 'Define
|
|
18954
|
+
return 'Define the effective agent **goal**; when multiple goals exist, only the last one stays effective.';
|
|
18914
18955
|
}
|
|
18915
18956
|
/**
|
|
18916
18957
|
* Icon for this commitment.
|
|
@@ -18925,12 +18966,14 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
18925
18966
|
return spaceTrim$1(`
|
|
18926
18967
|
# ${this.type}
|
|
18927
18968
|
|
|
18928
|
-
Defines the main goal which should be achieved by the AI assistant.
|
|
18969
|
+
Defines the main goal which should be achieved by the AI assistant.
|
|
18970
|
+
There can be multiple goals in source, but after inheritance/source rewriting only the last \`GOAL\` /\`GOALS\` remains effective.
|
|
18929
18971
|
|
|
18930
18972
|
## Key aspects
|
|
18931
18973
|
|
|
18932
18974
|
- Both terms work identically and can be used interchangeably.
|
|
18933
|
-
- Later goals
|
|
18975
|
+
- Later goals overwrite earlier goals.
|
|
18976
|
+
- The public agent profile text is derived from the last goal.
|
|
18934
18977
|
- Goals provide clear direction and purpose for the agent's responses.
|
|
18935
18978
|
- Goals influence decision-making and response prioritization.
|
|
18936
18979
|
|
|
@@ -18943,9 +18986,7 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
18943
18986
|
\`\`\`book
|
|
18944
18987
|
Customer Support Agent
|
|
18945
18988
|
|
|
18946
|
-
PERSONA You are a helpful customer support representative
|
|
18947
18989
|
GOAL Resolve customer issues quickly and efficiently
|
|
18948
|
-
GOAL Maintain high customer satisfaction scores
|
|
18949
18990
|
GOAL Always follow company policies and procedures
|
|
18950
18991
|
RULE Be polite and professional at all times
|
|
18951
18992
|
\`\`\`
|
|
@@ -18953,9 +18994,7 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
18953
18994
|
\`\`\`book
|
|
18954
18995
|
Educational Assistant
|
|
18955
18996
|
|
|
18956
|
-
PERSONA You are an educational assistant specializing in mathematics
|
|
18957
18997
|
GOAL Help students understand mathematical concepts clearly
|
|
18958
|
-
GOAL Encourage critical thinking and problem-solving skills
|
|
18959
18998
|
GOAL Ensure all explanations are age-appropriate and accessible
|
|
18960
18999
|
STYLE Use simple language and provide step-by-step explanations
|
|
18961
19000
|
\`\`\`
|
|
@@ -18963,9 +19002,7 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
18963
19002
|
\`\`\`book
|
|
18964
19003
|
Safety-First Assistant
|
|
18965
19004
|
|
|
18966
|
-
PERSONA You are a general-purpose AI assistant
|
|
18967
19005
|
GOAL Be helpful and informative in all interactions
|
|
18968
|
-
GOAL Provide accurate and reliable information
|
|
18969
19006
|
GOAL Always prioritize user safety and ethical guidelines
|
|
18970
19007
|
RULE Never provide harmful or dangerous advice
|
|
18971
19008
|
\`\`\`
|
|
@@ -21824,7 +21861,16 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
21824
21861
|
* Short one-line description of PERSONA.
|
|
21825
21862
|
*/
|
|
21826
21863
|
get description() {
|
|
21827
|
-
return '
|
|
21864
|
+
return 'Deprecated legacy profile commitment. Prefer `GOAL` for agent profile text and inheritance-safe rewrites.';
|
|
21865
|
+
}
|
|
21866
|
+
/**
|
|
21867
|
+
* Optional UI/docs-only deprecation metadata.
|
|
21868
|
+
*/
|
|
21869
|
+
get deprecation() {
|
|
21870
|
+
return {
|
|
21871
|
+
message: 'Use `GOAL` for agent profile text and inheritance-safe rewrites.',
|
|
21872
|
+
replacedBy: ['GOAL'],
|
|
21873
|
+
};
|
|
21828
21874
|
}
|
|
21829
21875
|
/**
|
|
21830
21876
|
* Icon for this commitment.
|
|
@@ -21839,16 +21885,24 @@ class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
21839
21885
|
return spaceTrim$1(`
|
|
21840
21886
|
# ${this.type}
|
|
21841
21887
|
|
|
21842
|
-
|
|
21888
|
+
Deprecated legacy commitment that defines who the agent is, their background, expertise, and personality traits.
|
|
21843
21889
|
|
|
21844
|
-
##
|
|
21890
|
+
## Migration
|
|
21845
21891
|
|
|
21846
|
-
-
|
|
21847
|
-
-
|
|
21848
|
-
-
|
|
21849
|
-
-
|
|
21892
|
+
- Existing \`${this.type}\` books still parse and compile.
|
|
21893
|
+
- New books should prefer \`GOAL\`.
|
|
21894
|
+
- Agent profile rendering now prefers the last \`GOAL\` and only falls back to \`${this.type}\` when no goal exists.
|
|
21895
|
+
- Runtime compilation keeps the legacy multi-\`PERSONA\` merge behavior for backward compatibility.
|
|
21850
21896
|
|
|
21851
|
-
##
|
|
21897
|
+
## Preferred replacement
|
|
21898
|
+
|
|
21899
|
+
\`\`\`book
|
|
21900
|
+
Programming Assistant
|
|
21901
|
+
|
|
21902
|
+
GOAL Help the user solve programming problems with practical TypeScript and React guidance.
|
|
21903
|
+
\`\`\`
|
|
21904
|
+
|
|
21905
|
+
## Legacy compatibility example
|
|
21852
21906
|
|
|
21853
21907
|
\`\`\`book
|
|
21854
21908
|
Programming Assistant
|
|
@@ -22556,6 +22610,15 @@ const teamToolFunctions = {};
|
|
|
22556
22610
|
* Map of team tool titles.
|
|
22557
22611
|
*/
|
|
22558
22612
|
const teamToolTitles = {};
|
|
22613
|
+
/**
|
|
22614
|
+
* Shared TEAM usage rules appended ahead of teammate listings.
|
|
22615
|
+
*
|
|
22616
|
+
* @private
|
|
22617
|
+
*/
|
|
22618
|
+
const TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES = [
|
|
22619
|
+
'- If a teammate is relevant to the request, consult that teammate using the matching tool.',
|
|
22620
|
+
'- Do not ask the user for information that a listed teammate can provide directly.',
|
|
22621
|
+
];
|
|
22559
22622
|
/**
|
|
22560
22623
|
* Constant for remote agents by Url.
|
|
22561
22624
|
*/
|
|
@@ -22604,7 +22667,7 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
22604
22667
|
\`\`\`book
|
|
22605
22668
|
Legal Assistant
|
|
22606
22669
|
|
|
22607
|
-
|
|
22670
|
+
GOAL Get expert software-development advice from the teammate when legal discussion needs technical context.
|
|
22608
22671
|
TEAM You can talk with http://localhost:4440/agents/GMw67JN8TXxN7y to discuss the legal aspects.
|
|
22609
22672
|
\`\`\`
|
|
22610
22673
|
`);
|
|
@@ -22645,12 +22708,9 @@ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
22645
22708
|
if (updatedTools.some((tool) => tool.name === entry.toolName)) {
|
|
22646
22709
|
continue;
|
|
22647
22710
|
}
|
|
22648
|
-
const toolDescription = entry.description
|
|
22649
|
-
? `Consult teammate ${entry.teammate.label}\n${entry.description}`
|
|
22650
|
-
: `Consult teammate ${entry.teammate.label}`;
|
|
22651
22711
|
updatedTools.push({
|
|
22652
22712
|
name: entry.toolName,
|
|
22653
|
-
description:
|
|
22713
|
+
description: buildTeamToolDescription(entry),
|
|
22654
22714
|
parameters: {
|
|
22655
22715
|
type: 'object',
|
|
22656
22716
|
properties: {
|
|
@@ -22719,22 +22779,72 @@ function resolveTeamTeammateLabels(teamContent, teammates) {
|
|
|
22719
22779
|
/**
|
|
22720
22780
|
* Builds the textual TEAM section body for the final system message.
|
|
22721
22781
|
*
|
|
22722
|
-
* Each teammate is listed with its tool name
|
|
22723
|
-
* Uses `spaceTrim` to ensure consistent whitespace and indentation.
|
|
22782
|
+
* Each teammate is listed with its tool name, TEAM instructions, and optional profile hints.
|
|
22724
22783
|
*/
|
|
22725
22784
|
function buildTeamSystemMessageBody(teamEntries) {
|
|
22726
|
-
const lines =
|
|
22727
|
-
|
|
22728
|
-
|
|
22729
|
-
|
|
22730
|
-
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
});
|
|
22785
|
+
const lines = [
|
|
22786
|
+
...TEAM_SYSTEM_MESSAGE_GUIDANCE_LINES,
|
|
22787
|
+
'',
|
|
22788
|
+
...teamEntries.map((entry, index) => {
|
|
22789
|
+
const toolLine = `${index + 1}) ${entry.teammate.label} tool \`${entry.toolName}\``;
|
|
22790
|
+
const detailLines = collectTeamEntryDetails(entry).map(formatTeamEntryDetailLine);
|
|
22791
|
+
return [toolLine, ...detailLines].join('\n');
|
|
22792
|
+
}),
|
|
22793
|
+
];
|
|
22736
22794
|
return lines.join('\n');
|
|
22737
22795
|
}
|
|
22796
|
+
/**
|
|
22797
|
+
* Builds the model-visible description for one teammate tool.
|
|
22798
|
+
*
|
|
22799
|
+
* @private
|
|
22800
|
+
*/
|
|
22801
|
+
function buildTeamToolDescription(entry) {
|
|
22802
|
+
const detailLines = collectTeamEntryDetails(entry).map(({ label, content }) => `${label}: ${content}`);
|
|
22803
|
+
return [`Consult teammate ${entry.teammate.label}`, ...detailLines].join('\n');
|
|
22804
|
+
}
|
|
22805
|
+
/**
|
|
22806
|
+
* Collects structured teammate details that should stay visible to the model.
|
|
22807
|
+
*
|
|
22808
|
+
* @private
|
|
22809
|
+
*/
|
|
22810
|
+
function collectTeamEntryDetails(entry) {
|
|
22811
|
+
var _a;
|
|
22812
|
+
const details = [];
|
|
22813
|
+
const instructions = entry.teammate.instructions.trim();
|
|
22814
|
+
const description = ((_a = entry.description) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
22815
|
+
if (instructions) {
|
|
22816
|
+
details.push({
|
|
22817
|
+
label: 'TEAM instructions',
|
|
22818
|
+
content: instructions,
|
|
22819
|
+
});
|
|
22820
|
+
}
|
|
22821
|
+
if (description) {
|
|
22822
|
+
details.push({
|
|
22823
|
+
label: 'Profile',
|
|
22824
|
+
content: description,
|
|
22825
|
+
});
|
|
22826
|
+
}
|
|
22827
|
+
return details;
|
|
22828
|
+
}
|
|
22829
|
+
/**
|
|
22830
|
+
* Formats one teammate detail line for the TEAM system-message section.
|
|
22831
|
+
*
|
|
22832
|
+
* @private
|
|
22833
|
+
*/
|
|
22834
|
+
function formatTeamEntryDetailLine(detail) {
|
|
22835
|
+
return indentMultilineText(`${detail.label}: ${detail.content}`, ' ');
|
|
22836
|
+
}
|
|
22837
|
+
/**
|
|
22838
|
+
* Indents all lines of one potentially multi-line text block.
|
|
22839
|
+
*
|
|
22840
|
+
* @private
|
|
22841
|
+
*/
|
|
22842
|
+
function indentMultilineText(text, prefix) {
|
|
22843
|
+
return text
|
|
22844
|
+
.split('\n')
|
|
22845
|
+
.map((line) => `${prefix}${line}`)
|
|
22846
|
+
.join('\n');
|
|
22847
|
+
}
|
|
22738
22848
|
/**
|
|
22739
22849
|
* Registers tool function and title for a teammate tool.
|
|
22740
22850
|
*/
|
|
@@ -40907,7 +41017,7 @@ function createEmptyAgentModelRequirements() {
|
|
|
40907
41017
|
systemMessage: '',
|
|
40908
41018
|
promptSuffix: '',
|
|
40909
41019
|
// modelName: 'gpt-5',
|
|
40910
|
-
modelName: '
|
|
41020
|
+
modelName: 'gpt-5.4-mini',
|
|
40911
41021
|
temperature: 0.7,
|
|
40912
41022
|
topP: 0.9,
|
|
40913
41023
|
topK: 50,
|
|
@@ -41303,6 +41413,15 @@ const COMMITMENTS_WITH_AGENT_REFERENCES = new Set(['FROM', 'IMPORT', 'IMPORTS',
|
|
|
41303
41413
|
* @private internal constant of `createAgentModelRequirementsWithCommitments`
|
|
41304
41414
|
*/
|
|
41305
41415
|
const DELETE_COMMITMENT_TYPES = new Set(['DELETE', 'CANCEL', 'DISCARD', 'REMOVE']);
|
|
41416
|
+
/**
|
|
41417
|
+
* Commitments whose earlier occurrences are overwritten by the last occurrence in source order.
|
|
41418
|
+
*
|
|
41419
|
+
* @private internal constant of `createAgentModelRequirementsWithCommitments`
|
|
41420
|
+
*/
|
|
41421
|
+
const OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE = new Map([
|
|
41422
|
+
['GOAL', 'GOAL'],
|
|
41423
|
+
['GOALS', 'GOAL'],
|
|
41424
|
+
]);
|
|
41306
41425
|
/**
|
|
41307
41426
|
* Regex pattern matching markdown horizontal lines that should not be copied into the final system message.
|
|
41308
41427
|
*
|
|
@@ -41353,7 +41472,7 @@ function getSafeReferenceCommitmentFallback(commitmentType, originalContent) {
|
|
|
41353
41472
|
*/
|
|
41354
41473
|
async function createAgentModelRequirementsWithCommitments(agentSource, modelName, options) {
|
|
41355
41474
|
const parseResult = parseAgentSourceWithCommitments(agentSource);
|
|
41356
|
-
const filteredCommitments = filterDeletedCommitments(parseResult.commitments);
|
|
41475
|
+
const filteredCommitments = filterOverwrittenCommitments(filterDeletedCommitments(parseResult.commitments));
|
|
41357
41476
|
let requirements = createInitialAgentModelRequirements(parseResult.agentName, modelName);
|
|
41358
41477
|
requirements = await applyCommitmentsToRequirements(requirements, filteredCommitments, options);
|
|
41359
41478
|
requirements = aggregateUseCommitmentSystemMessages(requirements, filteredCommitments);
|
|
@@ -41364,6 +41483,35 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
|
|
|
41364
41483
|
requirements = await applyPendingInlineKnowledgeSources(requirements, options === null || options === void 0 ? void 0 : options.inlineKnowledgeSourceUploader);
|
|
41365
41484
|
return finalizeRequirements(requirements);
|
|
41366
41485
|
}
|
|
41486
|
+
/**
|
|
41487
|
+
* Removes earlier commitments that are overwritten by later commitments of the same semantic group.
|
|
41488
|
+
*
|
|
41489
|
+
* This currently keeps only the last `GOAL` / `GOALS` commitment so inheritance rewrites
|
|
41490
|
+
* and multi-goal sources expose one effective goal to the runtime.
|
|
41491
|
+
*
|
|
41492
|
+
* @param commitments - Parsed commitments after DELETE-like filtering.
|
|
41493
|
+
* @returns Commitments with overwritten entries removed while preserving source order.
|
|
41494
|
+
*
|
|
41495
|
+
* @private internal utility of `createAgentModelRequirementsWithCommitments`
|
|
41496
|
+
*/
|
|
41497
|
+
function filterOverwrittenCommitments(commitments) {
|
|
41498
|
+
const seenOverwriteGroups = new Set();
|
|
41499
|
+
const keptCommitments = [];
|
|
41500
|
+
for (let index = commitments.length - 1; index >= 0; index--) {
|
|
41501
|
+
const commitment = commitments[index];
|
|
41502
|
+
const overwriteGroup = OVERWRITTEN_COMMITMENT_GROUP_BY_TYPE.get(commitment.type);
|
|
41503
|
+
if (!overwriteGroup) {
|
|
41504
|
+
keptCommitments.push(commitment);
|
|
41505
|
+
continue;
|
|
41506
|
+
}
|
|
41507
|
+
if (seenOverwriteGroups.has(overwriteGroup)) {
|
|
41508
|
+
continue;
|
|
41509
|
+
}
|
|
41510
|
+
seenOverwriteGroups.add(overwriteGroup);
|
|
41511
|
+
keptCommitments.push(commitment);
|
|
41512
|
+
}
|
|
41513
|
+
return keptCommitments.reverse();
|
|
41514
|
+
}
|
|
41367
41515
|
/**
|
|
41368
41516
|
* Creates the initial requirements object with the parsed agent name stored in metadata and an optional model override.
|
|
41369
41517
|
*
|
|
@@ -42006,7 +42154,7 @@ function createDefaultAgentName(agentSource) {
|
|
|
42006
42154
|
function parseAgentSource(agentSource) {
|
|
42007
42155
|
const parseResult = parseAgentSourceWithCommitments(agentSource);
|
|
42008
42156
|
const resolvedAgentName = parseResult.agentName || createDefaultAgentName(agentSource);
|
|
42009
|
-
const personaDescription =
|
|
42157
|
+
const personaDescription = extractAgentProfileText(parseResult.commitments);
|
|
42010
42158
|
const initialMessage = extractInitialMessage(parseResult.commitments);
|
|
42011
42159
|
const parsedProfile = extractParsedAgentProfile(parseResult.commitments);
|
|
42012
42160
|
ensureMetaFullname(parsedProfile.meta, resolvedAgentName);
|
|
@@ -42110,25 +42258,33 @@ const META_COMMITMENT_APPLIERS = {
|
|
|
42110
42258
|
*/
|
|
42111
42259
|
const LOCAL_AGENT_REFERENCE_PREFIXES = ['./', '../', '/'];
|
|
42112
42260
|
/**
|
|
42113
|
-
*
|
|
42261
|
+
* Resolves the public agent profile text from the last GOAL/GOALS commitment,
|
|
42262
|
+
* falling back to the deprecated PERSONA/PERSONAE commitments when no goal exists.
|
|
42114
42263
|
*
|
|
42115
42264
|
* @private internal utility of `parseAgentSource`
|
|
42116
42265
|
*/
|
|
42117
|
-
function
|
|
42118
|
-
let
|
|
42266
|
+
function extractAgentProfileText(commitments) {
|
|
42267
|
+
let goalDescription = '';
|
|
42268
|
+
let hasGoalDescription = false;
|
|
42269
|
+
let personaDescription = '';
|
|
42270
|
+
let hasPersonaDescription = false;
|
|
42119
42271
|
for (const commitment of commitments) {
|
|
42120
|
-
if (commitment.type
|
|
42121
|
-
|
|
42272
|
+
if (commitment.type === 'GOAL' || commitment.type === 'GOALS') {
|
|
42273
|
+
goalDescription = commitment.content;
|
|
42274
|
+
hasGoalDescription = true;
|
|
42122
42275
|
}
|
|
42123
|
-
if (
|
|
42124
|
-
personaDescription =
|
|
42125
|
-
|
|
42126
|
-
else {
|
|
42127
|
-
personaDescription += `\n\n${personaDescription}`;
|
|
42276
|
+
if (commitment.type === 'PERSONA' || commitment.type === 'PERSONAE') {
|
|
42277
|
+
personaDescription = commitment.content;
|
|
42278
|
+
hasPersonaDescription = true;
|
|
42128
42279
|
}
|
|
42129
|
-
personaDescription += commitment.content;
|
|
42130
42280
|
}
|
|
42131
|
-
|
|
42281
|
+
if (hasGoalDescription) {
|
|
42282
|
+
return goalDescription;
|
|
42283
|
+
}
|
|
42284
|
+
if (hasPersonaDescription) {
|
|
42285
|
+
return personaDescription;
|
|
42286
|
+
}
|
|
42287
|
+
return null;
|
|
42132
42288
|
}
|
|
42133
42289
|
/**
|
|
42134
42290
|
* Resolves the last INITIAL MESSAGE commitment, which is the public initial-message value.
|
|
@@ -42615,7 +42771,7 @@ async function createAgentModelRequirements(agentSource, modelName, availableMod
|
|
|
42615
42771
|
* Selects the best model using the preparePersona function
|
|
42616
42772
|
* This directly uses preparePersona to ensure DRY principle
|
|
42617
42773
|
*
|
|
42618
|
-
* @param agentSource The agent source to derive
|
|
42774
|
+
* @param agentSource The agent source to derive effective profile text from
|
|
42619
42775
|
* @param llmTools LLM tools for preparing persona
|
|
42620
42776
|
* @returns The name of the best selected model
|
|
42621
42777
|
*
|
|
@@ -42623,9 +42779,9 @@ async function createAgentModelRequirements(agentSource, modelName, availableMod
|
|
|
42623
42779
|
*/
|
|
42624
42780
|
async function selectBestModelUsingPersona(agentSource, llmTools) {
|
|
42625
42781
|
var _a;
|
|
42626
|
-
// Parse agent source to get
|
|
42782
|
+
// Parse agent source to get the effective profile description
|
|
42627
42783
|
const { agentName, personaDescription } = parseAgentSource(agentSource);
|
|
42628
|
-
// Use agent name as fallback if no
|
|
42784
|
+
// Use agent name as fallback if no profile description is available
|
|
42629
42785
|
const description = personaDescription || agentName || 'AI Agent';
|
|
42630
42786
|
try {
|
|
42631
42787
|
// Use preparePersona directly
|
|
@@ -44087,7 +44243,27 @@ var findRefactorCandidates$1 = /*#__PURE__*/Object.freeze({
|
|
|
44087
44243
|
/**
|
|
44088
44244
|
* CLI usage text for this script.
|
|
44089
44245
|
*/
|
|
44090
|
-
const USAGE = 'Usage: run-codex-prompts [--dry-run] [--agent <agent-name>] [--model <model>] [--context <context-or-file>] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--auto-migrate] [--allow-destructive-auto-migrate] [--no-wait] [--ignore-git-changes] [--no-normalize-line-endings] [--
|
|
44246
|
+
const USAGE = 'Usage: run-codex-prompts [--dry-run] [--agent <agent-name>] [--model <model>] [--context <context-or-file>] [--test <test-command...>] [--thinking-level <thinking-level>] [--priority <minimum-priority>] [--allow-credits] [--preserve-logs] [--auto-migrate] [--allow-destructive-auto-migrate] [--no-wait] [--ignore-git-changes] [--no-normalize-line-endings] [--auto-push]';
|
|
44247
|
+
/**
|
|
44248
|
+
* Top-level flags supported by this command.
|
|
44249
|
+
*/
|
|
44250
|
+
const KNOWN_OPTION_FLAGS = new Set([
|
|
44251
|
+
'--dry-run',
|
|
44252
|
+
'--agent',
|
|
44253
|
+
'--model',
|
|
44254
|
+
'--context',
|
|
44255
|
+
'--test',
|
|
44256
|
+
'--thinking-level',
|
|
44257
|
+
'--priority',
|
|
44258
|
+
'--allow-credits',
|
|
44259
|
+
'--preserve-logs',
|
|
44260
|
+
'--auto-migrate',
|
|
44261
|
+
'--allow-destructive-auto-migrate',
|
|
44262
|
+
'--no-wait',
|
|
44263
|
+
'--ignore-git-changes',
|
|
44264
|
+
'--no-normalize-line-endings',
|
|
44265
|
+
'--auto-push',
|
|
44266
|
+
]);
|
|
44091
44267
|
/**
|
|
44092
44268
|
* Parses CLI arguments into runner options.
|
|
44093
44269
|
*/
|
|
@@ -44108,6 +44284,8 @@ function parseRunOptions(args) {
|
|
|
44108
44284
|
}
|
|
44109
44285
|
const model = readOptionValue(args, '--model');
|
|
44110
44286
|
const context = readOptionValue(args, '--context');
|
|
44287
|
+
const hasTestCommandFlag = args.includes('--test');
|
|
44288
|
+
const testCommand = readVariadicOptionValue(args, '--test');
|
|
44111
44289
|
const hasThinkingLevelFlag = args.includes('--thinking-level');
|
|
44112
44290
|
const thinkingLevelValue = readOptionValue(args, '--thinking-level');
|
|
44113
44291
|
const hasPriorityFlag = args.includes('--priority');
|
|
@@ -44115,10 +44293,14 @@ function parseRunOptions(args) {
|
|
|
44115
44293
|
const ignoreGitChanges = args.includes('--ignore-git-changes');
|
|
44116
44294
|
const normalizeLineEndings = !args.includes('--no-normalize-line-endings');
|
|
44117
44295
|
const allowCredits = args.includes('--allow-credits');
|
|
44296
|
+
const preserveLogs = args.includes('--preserve-logs');
|
|
44118
44297
|
const autoMigrate = args.includes('--auto-migrate');
|
|
44119
44298
|
const allowDestructiveAutoMigrate = args.includes('--allow-destructive-auto-migrate');
|
|
44120
|
-
const
|
|
44299
|
+
const autoPush = args.includes('--auto-push');
|
|
44121
44300
|
let thinkingLevel;
|
|
44301
|
+
if (hasTestCommandFlag && testCommand === undefined) {
|
|
44302
|
+
exitWithUsageError('Missing value for --test. Use a shell command such as `npm run test` and quote it when it contains top-level CLI flags.');
|
|
44303
|
+
}
|
|
44122
44304
|
if (hasThinkingLevelFlag && thinkingLevelValue === undefined) {
|
|
44123
44305
|
exitWithUsageError(`Missing value for --thinking-level. Use one of: ${THINKING_LEVEL_VALUES.join(', ')}.`);
|
|
44124
44306
|
}
|
|
@@ -44137,12 +44319,14 @@ function parseRunOptions(args) {
|
|
|
44137
44319
|
ignoreGitChanges,
|
|
44138
44320
|
normalizeLineEndings,
|
|
44139
44321
|
allowCredits,
|
|
44322
|
+
preserveLogs,
|
|
44140
44323
|
autoMigrate,
|
|
44141
44324
|
allowDestructiveAutoMigrate,
|
|
44142
|
-
|
|
44325
|
+
autoPush,
|
|
44143
44326
|
agentName,
|
|
44144
44327
|
model,
|
|
44145
44328
|
context,
|
|
44329
|
+
testCommand,
|
|
44146
44330
|
thinkingLevel,
|
|
44147
44331
|
priority,
|
|
44148
44332
|
};
|
|
@@ -44157,6 +44341,28 @@ function readOptionValue(args, flag) {
|
|
|
44157
44341
|
const index = args.indexOf(flag);
|
|
44158
44342
|
return args[index + 1];
|
|
44159
44343
|
}
|
|
44344
|
+
/**
|
|
44345
|
+
* Reads a multi-token shell command value that follows a given flag.
|
|
44346
|
+
*/
|
|
44347
|
+
function readVariadicOptionValue(args, flag) {
|
|
44348
|
+
if (!args.includes(flag)) {
|
|
44349
|
+
return undefined;
|
|
44350
|
+
}
|
|
44351
|
+
const index = args.indexOf(flag);
|
|
44352
|
+
const valueParts = [];
|
|
44353
|
+
for (let i = index + 1; i < args.length; i++) {
|
|
44354
|
+
const valuePart = args[i];
|
|
44355
|
+
if (valuePart === undefined) {
|
|
44356
|
+
continue;
|
|
44357
|
+
}
|
|
44358
|
+
if (KNOWN_OPTION_FLAGS.has(valuePart)) {
|
|
44359
|
+
break;
|
|
44360
|
+
}
|
|
44361
|
+
valueParts.push(valuePart);
|
|
44362
|
+
}
|
|
44363
|
+
const normalizedValue = valueParts.join(' ').trim();
|
|
44364
|
+
return normalizedValue === '' ? undefined : normalizedValue;
|
|
44365
|
+
}
|
|
44160
44366
|
/**
|
|
44161
44367
|
* Parses and validates the minimum prompt priority.
|
|
44162
44368
|
*/
|
|
@@ -44182,6 +44388,21 @@ function exitWithUsageError(message) {
|
|
|
44182
44388
|
process.exit(1);
|
|
44183
44389
|
}
|
|
44184
44390
|
|
|
44391
|
+
/**
|
|
44392
|
+
* Appends optional coding context to a runner prompt.
|
|
44393
|
+
*/
|
|
44394
|
+
function appendCoderContext(prompt, context) {
|
|
44395
|
+
const normalizedContext = context === null || context === void 0 ? void 0 : context.trim();
|
|
44396
|
+
if (!normalizedContext) {
|
|
44397
|
+
return prompt;
|
|
44398
|
+
}
|
|
44399
|
+
const normalizedPrompt = prompt.trimEnd();
|
|
44400
|
+
if (normalizedPrompt === '') {
|
|
44401
|
+
return normalizedContext;
|
|
44402
|
+
}
|
|
44403
|
+
return `${normalizedPrompt}\n\n${normalizedContext}`;
|
|
44404
|
+
}
|
|
44405
|
+
|
|
44185
44406
|
/**
|
|
44186
44407
|
* Refresh interval for the progress header in milliseconds.
|
|
44187
44408
|
*/
|
|
@@ -44193,7 +44414,7 @@ const PROGRESS_HEADER_RESERVED_LINES = 1;
|
|
|
44193
44414
|
/**
|
|
44194
44415
|
* Calendar formats used when displaying the estimated completion time.
|
|
44195
44416
|
*/
|
|
44196
|
-
const ESTIMATED_DONE_CALENDAR_FORMATS = {
|
|
44417
|
+
const ESTIMATED_DONE_CALENDAR_FORMATS$1 = {
|
|
44197
44418
|
sameDay: '[Today] h:mm',
|
|
44198
44419
|
nextDay: '[Tomorrow] h:mm',
|
|
44199
44420
|
nextWeek: 'dddd h:mm',
|
|
@@ -44292,15 +44513,15 @@ function buildProgressSnapshot(stats, startTime, initialDone) {
|
|
|
44292
44513
|
const sessionTotal = sessionDone + stats.forAgent;
|
|
44293
44514
|
const percentage = totalPrompts > 0 ? Math.round((completedPrompts / totalPrompts) * 100) : 0;
|
|
44294
44515
|
const elapsedDuration = moment.duration(moment().diff(startTime));
|
|
44295
|
-
const elapsedText = formatDurationBrief(elapsedDuration);
|
|
44516
|
+
const elapsedText = formatDurationBrief$1(elapsedDuration);
|
|
44296
44517
|
let estimatedTotalText = '—';
|
|
44297
44518
|
let estimatedLabel = 'unknown';
|
|
44298
44519
|
if (totalPrompts > 0 && completedPrompts > 0) {
|
|
44299
44520
|
const estimatedTotalMs = (elapsedDuration.asMilliseconds() * totalPrompts) / completedPrompts;
|
|
44300
44521
|
const estimatedTotalDuration = moment.duration(estimatedTotalMs);
|
|
44301
44522
|
const estimatedCompletion = startTime.clone().add(estimatedTotalDuration);
|
|
44302
|
-
estimatedTotalText = formatDurationBrief(estimatedTotalDuration);
|
|
44303
|
-
estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS);
|
|
44523
|
+
estimatedTotalText = formatDurationBrief$1(estimatedTotalDuration);
|
|
44524
|
+
estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS$1);
|
|
44304
44525
|
}
|
|
44305
44526
|
return {
|
|
44306
44527
|
totalPrompts,
|
|
@@ -44316,7 +44537,7 @@ function buildProgressSnapshot(stats, startTime, initialDone) {
|
|
|
44316
44537
|
/**
|
|
44317
44538
|
* Formats a duration into a compact string such as "3h 12m" or "45s".
|
|
44318
44539
|
*/
|
|
44319
|
-
function formatDurationBrief(duration) {
|
|
44540
|
+
function formatDurationBrief$1(duration) {
|
|
44320
44541
|
const totalSeconds = Math.max(0, Math.round(duration.asSeconds()));
|
|
44321
44542
|
const hours = Math.floor(totalSeconds / 3600);
|
|
44322
44543
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
@@ -44337,21 +44558,6 @@ function formatDurationBrief(duration) {
|
|
|
44337
44558
|
return parts.join(' ');
|
|
44338
44559
|
}
|
|
44339
44560
|
|
|
44340
|
-
/**
|
|
44341
|
-
* Appends optional coding context to a runner prompt.
|
|
44342
|
-
*/
|
|
44343
|
-
function appendCoderContext(prompt, context) {
|
|
44344
|
-
const normalizedContext = context === null || context === void 0 ? void 0 : context.trim();
|
|
44345
|
-
if (!normalizedContext) {
|
|
44346
|
-
return prompt;
|
|
44347
|
-
}
|
|
44348
|
-
const normalizedPrompt = prompt.trimEnd();
|
|
44349
|
-
if (normalizedPrompt === '') {
|
|
44350
|
-
return normalizedContext;
|
|
44351
|
-
}
|
|
44352
|
-
return `${normalizedPrompt}\n\n${normalizedContext}`;
|
|
44353
|
-
}
|
|
44354
|
-
|
|
44355
44561
|
/**
|
|
44356
44562
|
* Git commands used to list changed and untracked files in the working tree.
|
|
44357
44563
|
*/
|
|
@@ -44607,6 +44813,91 @@ async function resolveCoderContext(contextReference, currentWorkingDirectory) {
|
|
|
44607
44813
|
return readFile(contextPath, 'utf-8');
|
|
44608
44814
|
}
|
|
44609
44815
|
|
|
44816
|
+
/**
|
|
44817
|
+
* Builds the temporary live runtime log path for one prompt script and its sibling test script.
|
|
44818
|
+
*/
|
|
44819
|
+
function buildScriptLogPath(scriptPath) {
|
|
44820
|
+
const basePath = scriptPath.replace(/\.test\.sh$/iu, '').replace(/\.sh$/iu, '');
|
|
44821
|
+
return `${basePath}.log.txt`;
|
|
44822
|
+
}
|
|
44823
|
+
|
|
44824
|
+
/**
|
|
44825
|
+
* Runs one prompt-processing round with a dedicated temporary runtime log file and optionally defers cleanup to the round tracker.
|
|
44826
|
+
*/
|
|
44827
|
+
async function withPromptRuntimeLog(scriptPath, handler, promptRoundArtifacts) {
|
|
44828
|
+
const logPath = buildScriptLogPath(scriptPath);
|
|
44829
|
+
await unlink(logPath).catch(() => undefined);
|
|
44830
|
+
promptRoundArtifacts === null || promptRoundArtifacts === void 0 ? void 0 : promptRoundArtifacts.track(logPath, 'runtime-log');
|
|
44831
|
+
try {
|
|
44832
|
+
return await handler(logPath);
|
|
44833
|
+
}
|
|
44834
|
+
finally {
|
|
44835
|
+
if (!promptRoundArtifacts) {
|
|
44836
|
+
await unlink(logPath).catch(() => undefined);
|
|
44837
|
+
}
|
|
44838
|
+
}
|
|
44839
|
+
}
|
|
44840
|
+
|
|
44841
|
+
/**
|
|
44842
|
+
* Shared artifact kinds preserved for debugging when a prompt round fails.
|
|
44843
|
+
*/
|
|
44844
|
+
const FAILURE_PRESERVED_ARTIFACT_KINDS = new Set(['runner-script', 'runtime-log']);
|
|
44845
|
+
/**
|
|
44846
|
+
* Shared artifact kinds preserved after a successful prompt round when explicitly requested.
|
|
44847
|
+
*/
|
|
44848
|
+
const SUCCESS_PRESERVED_ARTIFACT_KINDS = new Set(['runner-script', 'runtime-log']);
|
|
44849
|
+
/**
|
|
44850
|
+
* Empty preserved-artifact set used for successful rounds without `--preserve-logs`.
|
|
44851
|
+
*/
|
|
44852
|
+
const NO_PRESERVED_ARTIFACT_KINDS = new Set();
|
|
44853
|
+
/**
|
|
44854
|
+
* Tracks temporary prompt-round artifacts and deletes only those not preserved for the final round outcome.
|
|
44855
|
+
*/
|
|
44856
|
+
class PromptRoundArtifacts {
|
|
44857
|
+
/**
|
|
44858
|
+
* Creates a new prompt-round artifact tracker.
|
|
44859
|
+
*/
|
|
44860
|
+
constructor(preservedArtifactKindsByOutcome) {
|
|
44861
|
+
this.preservedArtifactKindsByOutcome = preservedArtifactKindsByOutcome;
|
|
44862
|
+
this.trackedArtifacts = new Map();
|
|
44863
|
+
}
|
|
44864
|
+
/**
|
|
44865
|
+
* Registers one temporary artifact for round-final cleanup.
|
|
44866
|
+
*/
|
|
44867
|
+
track(path, kind) {
|
|
44868
|
+
this.trackedArtifacts.set(path, kind);
|
|
44869
|
+
}
|
|
44870
|
+
/**
|
|
44871
|
+
* Cleans up all tracked artifacts that should not survive the final round outcome.
|
|
44872
|
+
*/
|
|
44873
|
+
async cleanup(outcome) {
|
|
44874
|
+
const preservedArtifactKinds = this.preservedArtifactKindsByOutcome[outcome];
|
|
44875
|
+
const trackedArtifacts = [...this.trackedArtifacts.entries()];
|
|
44876
|
+
this.trackedArtifacts.clear();
|
|
44877
|
+
await Promise.all(trackedArtifacts.map(async ([path, kind]) => {
|
|
44878
|
+
if (preservedArtifactKinds.has(kind)) {
|
|
44879
|
+
return;
|
|
44880
|
+
}
|
|
44881
|
+
await unlink(path).catch(() => undefined);
|
|
44882
|
+
}));
|
|
44883
|
+
}
|
|
44884
|
+
}
|
|
44885
|
+
/**
|
|
44886
|
+
* Creates the default artifact-retention policy used by `ptbk coder run`.
|
|
44887
|
+
*/
|
|
44888
|
+
function createCoderRunPromptRoundArtifacts(isPreserveLogs) {
|
|
44889
|
+
return new PromptRoundArtifacts({
|
|
44890
|
+
success: isPreserveLogs ? SUCCESS_PRESERVED_ARTIFACT_KINDS : NO_PRESERVED_ARTIFACT_KINDS,
|
|
44891
|
+
failure: FAILURE_PRESERVED_ARTIFACT_KINDS,
|
|
44892
|
+
});
|
|
44893
|
+
}
|
|
44894
|
+
/**
|
|
44895
|
+
* Derives the tracked artifact kind from one temporary shell path.
|
|
44896
|
+
*/
|
|
44897
|
+
function getPromptRoundArtifactKindFromScriptPath(scriptPath) {
|
|
44898
|
+
return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test-script' : 'runner-script';
|
|
44899
|
+
}
|
|
44900
|
+
|
|
44610
44901
|
/**
|
|
44611
44902
|
* Waits for the user to press Enter before continuing.
|
|
44612
44903
|
*/
|
|
@@ -44621,7 +44912,7 @@ async function waitForEnter(prompt) {
|
|
|
44621
44912
|
}
|
|
44622
44913
|
|
|
44623
44914
|
/**
|
|
44624
|
-
*
|
|
44915
|
+
* Current pause state.
|
|
44625
44916
|
*/
|
|
44626
44917
|
let pauseState = 'RUNNING';
|
|
44627
44918
|
/**
|
|
@@ -44658,17 +44949,48 @@ function listenForPause() {
|
|
|
44658
44949
|
}
|
|
44659
44950
|
/**
|
|
44660
44951
|
* If the execution is paused, it will wait until it is resumed.
|
|
44952
|
+
*
|
|
44953
|
+
* @param options.silent - When `true`, suppresses console output (used when the terminal UI handles display).
|
|
44954
|
+
* @param options.onPaused - Callback invoked when entering the PAUSED state.
|
|
44955
|
+
* @param options.onResumed - Callback invoked when leaving the PAUSED state.
|
|
44661
44956
|
*/
|
|
44662
|
-
async function checkPause() {
|
|
44957
|
+
async function checkPause(options) {
|
|
44958
|
+
var _a, _b;
|
|
44663
44959
|
if (pauseState === 'PAUSING') {
|
|
44664
44960
|
pauseState = 'PAUSED';
|
|
44665
|
-
|
|
44961
|
+
if (!(options === null || options === void 0 ? void 0 : options.silent)) {
|
|
44962
|
+
console.log(colors.bgWhite.black('Paused') + colors.gray(' (Press "p" to resume)'));
|
|
44963
|
+
}
|
|
44964
|
+
(_a = options === null || options === void 0 ? void 0 : options.onPaused) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
44666
44965
|
while (pauseState === 'PAUSED') {
|
|
44667
44966
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
44668
44967
|
}
|
|
44669
|
-
|
|
44968
|
+
(_b = options === null || options === void 0 ? void 0 : options.onResumed) === null || _b === void 0 ? void 0 : _b.call(options);
|
|
44969
|
+
if (!(options === null || options === void 0 ? void 0 : options.silent)) {
|
|
44970
|
+
console.log(colors.green('Resuming...'));
|
|
44971
|
+
}
|
|
44670
44972
|
}
|
|
44671
44973
|
}
|
|
44974
|
+
/**
|
|
44975
|
+
* Returns the current pause state for external consumers such as the terminal UI.
|
|
44976
|
+
*/
|
|
44977
|
+
function getPauseState() {
|
|
44978
|
+
return pauseState;
|
|
44979
|
+
}
|
|
44980
|
+
/**
|
|
44981
|
+
* Requests a pause from an external controller (e.g. the Ink UI).
|
|
44982
|
+
*/
|
|
44983
|
+
function requestPause() {
|
|
44984
|
+
if (pauseState === 'RUNNING') {
|
|
44985
|
+
pauseState = 'PAUSING';
|
|
44986
|
+
}
|
|
44987
|
+
}
|
|
44988
|
+
/**
|
|
44989
|
+
* Resumes execution from an external controller after a pause.
|
|
44990
|
+
*/
|
|
44991
|
+
function requestResume() {
|
|
44992
|
+
pauseState = 'RUNNING';
|
|
44993
|
+
}
|
|
44672
44994
|
|
|
44673
44995
|
/**
|
|
44674
44996
|
* Environment variable that configures the name used for agent commits.
|
|
@@ -45013,7 +45335,7 @@ function stringifyUnknownError$1(error) {
|
|
|
45013
45335
|
|
|
45014
45336
|
/**
|
|
45015
45337
|
* Commits staged changes with the provided message using the dedicated coding-agent identity when configured,
|
|
45016
|
-
* otherwise falls back to the default Git configuration.
|
|
45338
|
+
* otherwise falls back to the default Git configuration. Remote pushing is opt-in via `options.autoPush`.
|
|
45017
45339
|
*/
|
|
45018
45340
|
async function commitChanges(message, options) {
|
|
45019
45341
|
const projectPath = process.cwd();
|
|
@@ -45033,7 +45355,7 @@ async function commitChanges(message, options) {
|
|
|
45033
45355
|
cwd: projectPath,
|
|
45034
45356
|
env: agentEnv,
|
|
45035
45357
|
});
|
|
45036
|
-
if (
|
|
45358
|
+
if (options === null || options === void 0 ? void 0 : options.autoPush) {
|
|
45037
45359
|
await pushCommittedChanges(projectPath, agentEnv);
|
|
45038
45360
|
}
|
|
45039
45361
|
}
|
|
@@ -46626,6 +46948,19 @@ function formatUsagePrice(usage) {
|
|
|
46626
46948
|
return `${prefix}$${price.toFixed(2)}`;
|
|
46627
46949
|
}
|
|
46628
46950
|
|
|
46951
|
+
/**
|
|
46952
|
+
* Formats optional attempt metadata stored in prompt status lines.
|
|
46953
|
+
*/
|
|
46954
|
+
function formatPromptAttemptMetadata(status, attemptCount) {
|
|
46955
|
+
if (attemptCount <= 1) {
|
|
46956
|
+
return '';
|
|
46957
|
+
}
|
|
46958
|
+
if (status === 'done') {
|
|
46959
|
+
return `(${attemptCount} attempts) `;
|
|
46960
|
+
}
|
|
46961
|
+
return `(failed after ${attemptCount} attempts) `;
|
|
46962
|
+
}
|
|
46963
|
+
|
|
46629
46964
|
/**
|
|
46630
46965
|
* Formats runner details for prompt status lines.
|
|
46631
46966
|
*/
|
|
@@ -46645,7 +46980,7 @@ function formatRunnerSignature(runnerName, modelName) {
|
|
|
46645
46980
|
/**
|
|
46646
46981
|
* Marks a prompt section as done and records usage pricing and runner details.
|
|
46647
46982
|
*/
|
|
46648
|
-
function markPromptDone(file, section, usage, runnerName, modelName, promptExecutionStartedDate) {
|
|
46983
|
+
function markPromptDone(file, section, usage, runnerName, modelName, promptExecutionStartedDate, attemptCount = 1) {
|
|
46649
46984
|
if (section.statusLineIndex === undefined) {
|
|
46650
46985
|
throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
|
|
46651
46986
|
}
|
|
@@ -46655,16 +46990,17 @@ function markPromptDone(file, section, usage, runnerName, modelName, promptExecu
|
|
|
46655
46990
|
}
|
|
46656
46991
|
const priceString = formatUsagePrice(usage);
|
|
46657
46992
|
const runnerSignature = formatRunnerSignature(runnerName, modelName);
|
|
46993
|
+
const attemptMetadata = formatPromptAttemptMetadata('done', attemptCount);
|
|
46658
46994
|
const duration = moment().diff(promptExecutionStartedDate);
|
|
46659
46995
|
const durationString = moment.duration(duration).humanize();
|
|
46660
46996
|
// Replace "[ ]" or "[ ] !!..." with "[x] $price duration by runner"
|
|
46661
|
-
file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[x] ${priceString} ${durationString} by ${runnerSignature}`);
|
|
46997
|
+
file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[x] ${attemptMetadata}${priceString} ${durationString} by ${runnerSignature}`);
|
|
46662
46998
|
}
|
|
46663
46999
|
|
|
46664
47000
|
/**
|
|
46665
47001
|
* Marks a prompt section as failed and records runner details.
|
|
46666
47002
|
*/
|
|
46667
|
-
function markPromptFailed(file, section, runnerName, modelName, promptExecutionStartedDate) {
|
|
47003
|
+
function markPromptFailed(file, section, runnerName, modelName, promptExecutionStartedDate, attemptCount = 1) {
|
|
46668
47004
|
if (section.statusLineIndex === undefined) {
|
|
46669
47005
|
throw new Error(`Prompt ${section.index + 1} in ${file.name} does not have a status line.`);
|
|
46670
47006
|
}
|
|
@@ -46673,9 +47009,11 @@ function markPromptFailed(file, section, runnerName, modelName, promptExecutionS
|
|
|
46673
47009
|
throw new Error(`Prompt ${section.index + 1} in ${file.name} points to a missing status line.`);
|
|
46674
47010
|
}
|
|
46675
47011
|
const runnerSignature = formatRunnerSignature(runnerName, modelName);
|
|
47012
|
+
const attemptMetadata = formatPromptAttemptMetadata('failed', attemptCount);
|
|
46676
47013
|
const duration = moment().diff(promptExecutionStartedDate);
|
|
46677
47014
|
const durationString = moment.duration(duration).humanize();
|
|
46678
|
-
|
|
47015
|
+
const failureDetails = attemptMetadata === '' ? `failed after ${durationString} by ${runnerSignature}` : `${attemptMetadata}${durationString} by ${runnerSignature}`;
|
|
47016
|
+
file.lines[section.statusLineIndex] = line.replace(/\[\s*\]\s*!*\s*$/, `[!] ${failureDetails}`);
|
|
46679
47017
|
}
|
|
46680
47018
|
|
|
46681
47019
|
/**
|
|
@@ -46792,6 +47130,20 @@ async function waitForPromptStart(file, section, isFirstPrompt) {
|
|
|
46792
47130
|
await waitForEnter(colors.bgWhite(`Press Enter to start the ${label}...`));
|
|
46793
47131
|
}
|
|
46794
47132
|
|
|
47133
|
+
/**
|
|
47134
|
+
* Formats one unknown error-like value into readable text for logs and feedback.
|
|
47135
|
+
*/
|
|
47136
|
+
function formatUnknownErrorDetails(error) {
|
|
47137
|
+
if (error instanceof Error) {
|
|
47138
|
+
return error.stack || error.message;
|
|
47139
|
+
}
|
|
47140
|
+
if (typeof error === 'string') {
|
|
47141
|
+
return error;
|
|
47142
|
+
}
|
|
47143
|
+
const serializedError = JSON.stringify(error, null, 2);
|
|
47144
|
+
return serializedError !== null && serializedError !== void 0 ? serializedError : String(error);
|
|
47145
|
+
}
|
|
47146
|
+
|
|
46795
47147
|
/**
|
|
46796
47148
|
* Writes the failure details for a single prompt run next to the prompt markdown file.
|
|
46797
47149
|
*/
|
|
@@ -46799,7 +47151,7 @@ async function writePromptErrorLog(options) {
|
|
|
46799
47151
|
const logPath = buildPromptErrorLogPath(options.file.path);
|
|
46800
47152
|
const label = buildPromptLabelForDisplay(options.file, options.section);
|
|
46801
47153
|
const summary = buildPromptSummary(options.file, options.section);
|
|
46802
|
-
const details =
|
|
47154
|
+
const details = formatUnknownErrorDetails(options.error);
|
|
46803
47155
|
const modelSuffix = options.modelName ? ` (${options.modelName})` : '';
|
|
46804
47156
|
const runnerLabel = `${options.runnerName || 'unknown'}${modelSuffix}`;
|
|
46805
47157
|
const log = [
|
|
@@ -46826,18 +47178,6 @@ function buildPromptErrorLogPath(promptPath) {
|
|
|
46826
47178
|
}
|
|
46827
47179
|
return `${promptPath}.error.log`;
|
|
46828
47180
|
}
|
|
46829
|
-
/**
|
|
46830
|
-
* Formats unknown error values into a readable log payload.
|
|
46831
|
-
*/
|
|
46832
|
-
function buildErrorDetails(error) {
|
|
46833
|
-
if (error instanceof Error) {
|
|
46834
|
-
return error.stack || error.message;
|
|
46835
|
-
}
|
|
46836
|
-
if (typeof error === 'string') {
|
|
46837
|
-
return error;
|
|
46838
|
-
}
|
|
46839
|
-
return JSON.stringify(error, null, 2);
|
|
46840
|
-
}
|
|
46841
47181
|
|
|
46842
47182
|
/**
|
|
46843
47183
|
* Writes updated prompt file content to disk.
|
|
@@ -46861,25 +47201,186 @@ function toPosixPath(filePath) {
|
|
|
46861
47201
|
}
|
|
46862
47202
|
|
|
46863
47203
|
/**
|
|
46864
|
-
*
|
|
47204
|
+
* Environment variable read by the shell wrapper to tee live output into the temporary runtime log file.
|
|
47205
|
+
*/
|
|
47206
|
+
const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
|
|
47207
|
+
/**
|
|
47208
|
+
* Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
|
|
47209
|
+
*/
|
|
47210
|
+
const LOGGED_BASH_WRAPPER_COMMAND = `
|
|
47211
|
+
if [ -n "\${${PTBK_CODER_LOG_FILE_ENV_NAME}:-}" ]; then
|
|
47212
|
+
exec > >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "\$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
|
|
47213
|
+
fi
|
|
47214
|
+
bash "$1"
|
|
47215
|
+
`.trim();
|
|
47216
|
+
/**
|
|
47217
|
+
* Shapes one bash invocation that optionally mirrors live script output into a temporary log file.
|
|
47218
|
+
*/
|
|
47219
|
+
function buildLoggedBashExecution(scriptPath, logPath) {
|
|
47220
|
+
return {
|
|
47221
|
+
args: ['-lc', LOGGED_BASH_WRAPPER_COMMAND, 'ptbk-coder-temp-script', toPosixPath(scriptPath)],
|
|
47222
|
+
env: logPath ? { [PTBK_CODER_LOG_FILE_ENV_NAME]: toPosixPath(logPath) } : undefined,
|
|
47223
|
+
};
|
|
47224
|
+
}
|
|
47225
|
+
/**
|
|
47226
|
+
* Appends one execution-start section with the raw script input before the shell begins producing output.
|
|
47227
|
+
*/
|
|
47228
|
+
async function appendScriptExecutionLogStart({ scriptPath, scriptContent, logPath, }) {
|
|
47229
|
+
if (!logPath) {
|
|
47230
|
+
return;
|
|
47231
|
+
}
|
|
47232
|
+
await mkdir(dirname(logPath), { recursive: true });
|
|
47233
|
+
const scriptKind = describeTempScriptKind(scriptPath);
|
|
47234
|
+
const normalizedInput = scriptContent.replace(/\r\n/g, '\n').trimEnd();
|
|
47235
|
+
const logSection = [
|
|
47236
|
+
`=== ${scriptKind} started at ${new Date().toISOString()} ===`,
|
|
47237
|
+
`Script path: ${toPosixPath(scriptPath)}`,
|
|
47238
|
+
'',
|
|
47239
|
+
'--- raw input ---',
|
|
47240
|
+
normalizedInput,
|
|
47241
|
+
'',
|
|
47242
|
+
'--- raw output ---',
|
|
47243
|
+
'',
|
|
47244
|
+
].join('\n');
|
|
47245
|
+
await appendFile(logPath, `${logSection}\n`, 'utf-8');
|
|
47246
|
+
}
|
|
47247
|
+
/**
|
|
47248
|
+
* Appends one execution-finish section after the shell settles.
|
|
47249
|
+
*/
|
|
47250
|
+
async function appendScriptExecutionLogFinish({ scriptPath, logPath, status, details, }) {
|
|
47251
|
+
if (!logPath) {
|
|
47252
|
+
return;
|
|
47253
|
+
}
|
|
47254
|
+
const scriptKind = describeTempScriptKind(scriptPath);
|
|
47255
|
+
const logLines = [
|
|
47256
|
+
'',
|
|
47257
|
+
`=== ${scriptKind} finished at ${new Date().toISOString()} ===`,
|
|
47258
|
+
`Status: ${status}`,
|
|
47259
|
+
];
|
|
47260
|
+
if (details !== undefined) {
|
|
47261
|
+
logLines.push('');
|
|
47262
|
+
logLines.push('--- details ---');
|
|
47263
|
+
logLines.push(formatUnknownErrorDetails(details));
|
|
47264
|
+
}
|
|
47265
|
+
logLines.push('');
|
|
47266
|
+
await appendFile(logPath, `${logLines.join('\n')}\n`, 'utf-8');
|
|
47267
|
+
}
|
|
47268
|
+
/**
|
|
47269
|
+
* Distinguishes prompt-runner and verification temp shells in the shared runtime log.
|
|
47270
|
+
*/
|
|
47271
|
+
function describeTempScriptKind(scriptPath) {
|
|
47272
|
+
return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
|
|
47273
|
+
}
|
|
47274
|
+
|
|
47275
|
+
/**
|
|
47276
|
+
* Runs one temporary bash script, optionally mirroring its raw input/output into a live runtime log file.
|
|
47277
|
+
*/
|
|
47278
|
+
async function runBashScriptWithOutput(options) {
|
|
47279
|
+
await appendScriptExecutionLogStart(options);
|
|
47280
|
+
const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
|
|
47281
|
+
const scriptPathPosix = toPosixPath(options.scriptPath);
|
|
47282
|
+
return await new Promise((resolve, reject) => {
|
|
47283
|
+
const commandProcess = spawn('bash', bashExecution.args, {
|
|
47284
|
+
env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
|
|
47285
|
+
});
|
|
47286
|
+
let output = '';
|
|
47287
|
+
let settled = false;
|
|
47288
|
+
let isSettling = false;
|
|
47289
|
+
/**
|
|
47290
|
+
* Appends the final log footer before settling.
|
|
47291
|
+
*/
|
|
47292
|
+
const finishLog = async (status, details) => {
|
|
47293
|
+
await appendScriptExecutionLogFinish({
|
|
47294
|
+
scriptPath: options.scriptPath,
|
|
47295
|
+
logPath: options.logPath,
|
|
47296
|
+
status,
|
|
47297
|
+
details,
|
|
47298
|
+
});
|
|
47299
|
+
};
|
|
47300
|
+
/**
|
|
47301
|
+
* Ensures the promise settles only once.
|
|
47302
|
+
*/
|
|
47303
|
+
const settleOnce = (handler) => {
|
|
47304
|
+
if (settled) {
|
|
47305
|
+
return;
|
|
47306
|
+
}
|
|
47307
|
+
settled = true;
|
|
47308
|
+
handler();
|
|
47309
|
+
};
|
|
47310
|
+
/**
|
|
47311
|
+
* Appends the final log footer and settles the promise exactly once.
|
|
47312
|
+
*/
|
|
47313
|
+
const settleWithLog = (status, handler, details) => {
|
|
47314
|
+
if (isSettling || settled) {
|
|
47315
|
+
return;
|
|
47316
|
+
}
|
|
47317
|
+
isSettling = true;
|
|
47318
|
+
void finishLog(status, details).finally(() => {
|
|
47319
|
+
settleOnce(handler);
|
|
47320
|
+
});
|
|
47321
|
+
};
|
|
47322
|
+
commandProcess.stdout.on('data', (stdout) => {
|
|
47323
|
+
const chunk = stdout.toString();
|
|
47324
|
+
output += chunk;
|
|
47325
|
+
console.info(chunk);
|
|
47326
|
+
});
|
|
47327
|
+
commandProcess.stderr.on('data', (stderr) => {
|
|
47328
|
+
const chunk = stderr.toString();
|
|
47329
|
+
output += chunk;
|
|
47330
|
+
if (chunk.trim()) {
|
|
47331
|
+
console.warn(chunk);
|
|
47332
|
+
}
|
|
47333
|
+
});
|
|
47334
|
+
/**
|
|
47335
|
+
* Handles process exit and resolves or rejects accordingly.
|
|
47336
|
+
*/
|
|
47337
|
+
const handleExit = (code) => {
|
|
47338
|
+
if (code === 0) {
|
|
47339
|
+
settleWithLog('succeeded', () => resolve(spaceTrim$1(output)));
|
|
47340
|
+
return;
|
|
47341
|
+
}
|
|
47342
|
+
const failure = new Error(spaceTrim$1(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
|
|
47343
|
+
settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
|
|
47344
|
+
};
|
|
47345
|
+
commandProcess.on('close', handleExit);
|
|
47346
|
+
commandProcess.on('exit', handleExit);
|
|
47347
|
+
commandProcess.on('disconnect', () => {
|
|
47348
|
+
const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
|
|
47349
|
+
settleWithLog('failed after disconnect', () => reject(failure), failure);
|
|
47350
|
+
});
|
|
47351
|
+
commandProcess.on('error', (error) => {
|
|
47352
|
+
const failure = new Error(`Command "bash ${scriptPathPosix}" failed: ${error.message}`);
|
|
47353
|
+
settleWithLog('failed before completion', () => reject(failure), failure);
|
|
47354
|
+
});
|
|
47355
|
+
});
|
|
47356
|
+
}
|
|
47357
|
+
|
|
47358
|
+
/**
|
|
47359
|
+
* Creates a temporary script file, runs a handler, and either cleans it up immediately or defers cleanup to the round tracker.
|
|
46865
47360
|
*/
|
|
46866
47361
|
async function withTempScript(options, handler) {
|
|
46867
|
-
const { scriptPath, scriptContent } = options;
|
|
47362
|
+
const { scriptPath, scriptContent, promptRoundArtifacts } = options;
|
|
46868
47363
|
await mkdir(dirname$1(scriptPath), { recursive: true });
|
|
46869
47364
|
await writeFile(scriptPath, scriptContent, 'utf-8');
|
|
46870
|
-
|
|
46871
|
-
|
|
46872
|
-
|
|
47365
|
+
promptRoundArtifacts === null || promptRoundArtifacts === void 0 ? void 0 : promptRoundArtifacts.track(scriptPath, getPromptRoundArtifactKindFromScriptPath(scriptPath));
|
|
47366
|
+
try {
|
|
47367
|
+
return await handler(scriptPath);
|
|
47368
|
+
}
|
|
47369
|
+
finally {
|
|
47370
|
+
if (!promptRoundArtifacts) {
|
|
47371
|
+
await unlink(scriptPath).catch(() => undefined);
|
|
47372
|
+
}
|
|
47373
|
+
}
|
|
46873
47374
|
}
|
|
46874
47375
|
|
|
46875
47376
|
/**
|
|
46876
|
-
* Creates a temporary script file, runs it, captures output, and
|
|
47377
|
+
* Creates a temporary script file, runs it, captures output, and cleans it up immediately unless a round tracker defers that cleanup.
|
|
46877
47378
|
*/
|
|
46878
47379
|
async function $runGoScriptWithOutput(options) {
|
|
46879
47380
|
return await withTempScript(options, async (scriptPath) => {
|
|
46880
|
-
return await
|
|
46881
|
-
|
|
46882
|
-
|
|
47381
|
+
return await runBashScriptWithOutput({
|
|
47382
|
+
...options,
|
|
47383
|
+
scriptPath,
|
|
46883
47384
|
});
|
|
46884
47385
|
});
|
|
46885
47386
|
}
|
|
@@ -46975,6 +47476,8 @@ class ClaudeCodeRunner {
|
|
|
46975
47476
|
const output = await $runGoScriptWithOutput({
|
|
46976
47477
|
scriptPath: options.scriptPath,
|
|
46977
47478
|
scriptContent,
|
|
47479
|
+
logPath: options.logPath,
|
|
47480
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
46978
47481
|
});
|
|
46979
47482
|
const usage = parseClaudeCodeJsonOutput(output);
|
|
46980
47483
|
return { usage };
|
|
@@ -46982,13 +47485,13 @@ class ClaudeCodeRunner {
|
|
|
46982
47485
|
}
|
|
46983
47486
|
|
|
46984
47487
|
/**
|
|
46985
|
-
* Creates a temporary script file, runs it, and
|
|
47488
|
+
* Creates a temporary script file, runs it, and cleans it up immediately unless a round tracker defers that cleanup.
|
|
46986
47489
|
*/
|
|
46987
47490
|
async function $runGoScript(options) {
|
|
46988
47491
|
await withTempScript(options, async (scriptPath) => {
|
|
46989
|
-
await
|
|
46990
|
-
|
|
46991
|
-
|
|
47492
|
+
await runBashScriptWithOutput({
|
|
47493
|
+
...options,
|
|
47494
|
+
scriptPath,
|
|
46992
47495
|
});
|
|
46993
47496
|
});
|
|
46994
47497
|
}
|
|
@@ -47037,6 +47540,8 @@ class ClineRunner {
|
|
|
47037
47540
|
await $runGoScript({
|
|
47038
47541
|
scriptPath: options.scriptPath,
|
|
47039
47542
|
scriptContent,
|
|
47543
|
+
logPath: options.logPath,
|
|
47544
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
47040
47545
|
});
|
|
47041
47546
|
return { usage: UNCERTAIN_USAGE };
|
|
47042
47547
|
}
|
|
@@ -47160,6 +47665,8 @@ class GeminiRunner {
|
|
|
47160
47665
|
const output = await $runGoScriptWithOutput({
|
|
47161
47666
|
scriptPath: options.scriptPath,
|
|
47162
47667
|
scriptContent,
|
|
47668
|
+
logPath: options.logPath,
|
|
47669
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
47163
47670
|
});
|
|
47164
47671
|
const usage = parseGeminiUsageFromOutput(output, options.prompt, this.options.model);
|
|
47165
47672
|
return { usage };
|
|
@@ -47215,6 +47722,8 @@ class GitHubCopilotRunner {
|
|
|
47215
47722
|
await $runGoScript({
|
|
47216
47723
|
scriptPath: options.scriptPath,
|
|
47217
47724
|
scriptContent,
|
|
47725
|
+
logPath: options.logPath,
|
|
47726
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
47218
47727
|
});
|
|
47219
47728
|
return { usage: UNCERTAIN_USAGE };
|
|
47220
47729
|
}
|
|
@@ -47251,14 +47760,30 @@ function buildCommandFailureMessage(scriptPathPosix, code, fullOutput) {
|
|
|
47251
47760
|
async function runScriptUntilMarkerIdle(options) {
|
|
47252
47761
|
const { scriptPath, completionLineMatcher, idleTimeoutMs } = options;
|
|
47253
47762
|
const scriptPathPosix = toPosixPath(scriptPath);
|
|
47763
|
+
await appendScriptExecutionLogStart(options);
|
|
47764
|
+
const bashExecution = buildLoggedBashExecution(scriptPath, options.logPath);
|
|
47254
47765
|
return await new Promise((resolve, reject) => {
|
|
47255
|
-
const commandProcess = spawn('bash',
|
|
47766
|
+
const commandProcess = spawn('bash', bashExecution.args, {
|
|
47767
|
+
env: bashExecution.env ? { ...process.env, ...bashExecution.env } : process.env,
|
|
47768
|
+
});
|
|
47256
47769
|
let stdoutBuffer = '';
|
|
47257
47770
|
let stderrBuffer = '';
|
|
47258
47771
|
let fullOutput = '';
|
|
47259
47772
|
let markerSeen = false;
|
|
47260
47773
|
let idleTimer;
|
|
47261
47774
|
let settled = false;
|
|
47775
|
+
let isSettling = false;
|
|
47776
|
+
/**
|
|
47777
|
+
* Appends the final log footer before settling.
|
|
47778
|
+
*/
|
|
47779
|
+
const finishLog = async (status, details) => {
|
|
47780
|
+
await appendScriptExecutionLogFinish({
|
|
47781
|
+
scriptPath,
|
|
47782
|
+
logPath: options.logPath,
|
|
47783
|
+
status,
|
|
47784
|
+
details,
|
|
47785
|
+
});
|
|
47786
|
+
};
|
|
47262
47787
|
/**
|
|
47263
47788
|
* Ensures the promise settles only once.
|
|
47264
47789
|
*/
|
|
@@ -47273,6 +47798,18 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
47273
47798
|
}
|
|
47274
47799
|
handler();
|
|
47275
47800
|
};
|
|
47801
|
+
/**
|
|
47802
|
+
* Appends the final log footer and settles the promise exactly once.
|
|
47803
|
+
*/
|
|
47804
|
+
const settleWithLog = (status, handler, details) => {
|
|
47805
|
+
if (isSettling || settled) {
|
|
47806
|
+
return;
|
|
47807
|
+
}
|
|
47808
|
+
isSettling = true;
|
|
47809
|
+
void finishLog(status, details).finally(() => {
|
|
47810
|
+
settleOnce(handler);
|
|
47811
|
+
});
|
|
47812
|
+
};
|
|
47276
47813
|
/**
|
|
47277
47814
|
* Resets the idle timer that triggers termination after inactivity.
|
|
47278
47815
|
*/
|
|
@@ -47282,7 +47819,7 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
47282
47819
|
}
|
|
47283
47820
|
idleTimer = setTimeout(() => {
|
|
47284
47821
|
commandProcess.kill();
|
|
47285
|
-
|
|
47822
|
+
settleWithLog('completed after idle timeout', () => resolve(fullOutput));
|
|
47286
47823
|
}, idleTimeoutMs);
|
|
47287
47824
|
};
|
|
47288
47825
|
/**
|
|
@@ -47336,41 +47873,43 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
47336
47873
|
* Handles process exit and resolves or rejects accordingly.
|
|
47337
47874
|
*/
|
|
47338
47875
|
const handleExit = (code) => {
|
|
47339
|
-
|
|
47340
|
-
|
|
47876
|
+
const failure = code === 0 || markerSeen ? undefined : new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
|
|
47877
|
+
const status = failure ? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}` : 'succeeded';
|
|
47878
|
+
settleWithLog(status, () => {
|
|
47879
|
+
if (!failure) {
|
|
47341
47880
|
resolve(fullOutput);
|
|
47342
47881
|
return;
|
|
47343
47882
|
}
|
|
47344
|
-
reject(
|
|
47883
|
+
reject(failure);
|
|
47345
47884
|
});
|
|
47346
47885
|
};
|
|
47347
47886
|
commandProcess.on('close', handleExit);
|
|
47348
47887
|
commandProcess.on('exit', handleExit);
|
|
47349
47888
|
commandProcess.on('disconnect', () => {
|
|
47350
|
-
|
|
47351
|
-
|
|
47352
|
-
});
|
|
47889
|
+
const failure = new Error(buildCommandFailureMessage(scriptPathPosix, null, fullOutput));
|
|
47890
|
+
settleWithLog('failed after disconnect', () => reject(failure), failure);
|
|
47353
47891
|
});
|
|
47354
47892
|
commandProcess.on('error', (error) => {
|
|
47355
|
-
|
|
47356
|
-
|
|
47357
|
-
|
|
47358
|
-
|
|
47359
|
-
});
|
|
47893
|
+
const outputSnippet = createOutputSnippet(fullOutput);
|
|
47894
|
+
const details = outputSnippet ? `\n\n${outputSnippet}` : '';
|
|
47895
|
+
const failure = new Error(`Command "bash ${scriptPathPosix}" failed: ${error.message}${details}`);
|
|
47896
|
+
settleWithLog('failed before completion', () => reject(failure), failure);
|
|
47360
47897
|
});
|
|
47361
47898
|
});
|
|
47362
47899
|
}
|
|
47363
47900
|
|
|
47364
47901
|
/**
|
|
47365
|
-
* Creates a temporary script file, runs it, waits for a completion marker and idle time, and
|
|
47902
|
+
* Creates a temporary script file, runs it, waits for a completion marker and idle time, and defers cleanup when a round tracker is provided.
|
|
47366
47903
|
* Returns the captured output for post-processing.
|
|
47367
47904
|
*/
|
|
47368
47905
|
async function $runGoScriptUntilMarkerIdle(options) {
|
|
47369
47906
|
return await withTempScript(options, async (scriptPath) => {
|
|
47370
47907
|
return await runScriptUntilMarkerIdle({
|
|
47371
47908
|
scriptPath,
|
|
47909
|
+
scriptContent: options.scriptContent,
|
|
47372
47910
|
completionLineMatcher: options.completionLineMatcher,
|
|
47373
47911
|
idleTimeoutMs: options.idleTimeoutMs,
|
|
47912
|
+
logPath: options.logPath,
|
|
47374
47913
|
});
|
|
47375
47914
|
});
|
|
47376
47915
|
}
|
|
@@ -47748,6 +48287,8 @@ class OpenAiCodexRunner {
|
|
|
47748
48287
|
scriptContent,
|
|
47749
48288
|
completionLineMatcher: CODEX_COMPLETION_LINE,
|
|
47750
48289
|
idleTimeoutMs: CODEX_COMPLETION_IDLE_MS,
|
|
48290
|
+
logPath: options.logPath,
|
|
48291
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
47751
48292
|
});
|
|
47752
48293
|
this.rateLimitBackoff.reset();
|
|
47753
48294
|
return { usage: buildCodexUsageFromOutput(output, this.options.model) };
|
|
@@ -47881,6 +48422,8 @@ class OpencodeRunner {
|
|
|
47881
48422
|
output = await $runGoScriptWithOutput({
|
|
47882
48423
|
scriptPath: options.scriptPath,
|
|
47883
48424
|
scriptContent,
|
|
48425
|
+
logPath: options.logPath,
|
|
48426
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
47884
48427
|
});
|
|
47885
48428
|
}
|
|
47886
48429
|
catch (error) {
|
|
@@ -47896,6 +48439,653 @@ class OpencodeRunner {
|
|
|
47896
48439
|
}
|
|
47897
48440
|
}
|
|
47898
48441
|
|
|
48442
|
+
/**
|
|
48443
|
+
* Runs the configured verification command inside the project root and returns its output.
|
|
48444
|
+
*/
|
|
48445
|
+
async function runPromptTestCommand(options) {
|
|
48446
|
+
const projectPath = toPosixPath(options.projectPath);
|
|
48447
|
+
return await $runGoScriptWithOutput({
|
|
48448
|
+
scriptPath: options.scriptPath,
|
|
48449
|
+
scriptContent: spaceTrim(`
|
|
48450
|
+
cd "${projectPath}"
|
|
48451
|
+
${options.command}
|
|
48452
|
+
`),
|
|
48453
|
+
logPath: options.logPath,
|
|
48454
|
+
});
|
|
48455
|
+
}
|
|
48456
|
+
|
|
48457
|
+
/**
|
|
48458
|
+
* Maximum number of coding attempts allowed for the same prompt when verification keeps failing.
|
|
48459
|
+
*/
|
|
48460
|
+
const MAX_PROMPT_TEST_ATTEMPTS = 3;
|
|
48461
|
+
/**
|
|
48462
|
+
* Maximum amount of verification output sent back to the coding agent as retry feedback.
|
|
48463
|
+
*/
|
|
48464
|
+
const MAX_TEST_FEEDBACK_OUTPUT_CHARS = 12000;
|
|
48465
|
+
/**
|
|
48466
|
+
* Runs one coding prompt and, when configured, verifies it with a shell command that can feed failures back.
|
|
48467
|
+
*/
|
|
48468
|
+
async function runPromptWithTestFeedback(options) {
|
|
48469
|
+
var _a, _b, _c, _d;
|
|
48470
|
+
const normalizedTestCommand = (_a = options.testCommand) === null || _a === void 0 ? void 0 : _a.trim();
|
|
48471
|
+
if (!normalizedTestCommand) {
|
|
48472
|
+
(_b = options.onAttemptStarted) === null || _b === void 0 ? void 0 : _b.call(options, 1);
|
|
48473
|
+
const result = await options.runner.runPrompt({
|
|
48474
|
+
prompt: options.prompt,
|
|
48475
|
+
scriptPath: options.scriptPath,
|
|
48476
|
+
projectPath: options.projectPath,
|
|
48477
|
+
logPath: options.logPath,
|
|
48478
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
48479
|
+
});
|
|
48480
|
+
return { ...result, attemptCount: 1 };
|
|
48481
|
+
}
|
|
48482
|
+
const runPromptTestCommandExecutor = (_c = options.runPromptTestCommandExecutor) !== null && _c !== void 0 ? _c : runPromptTestCommand;
|
|
48483
|
+
let promptForCurrentAttempt = options.prompt;
|
|
48484
|
+
for (let attemptCount = 1; attemptCount <= MAX_PROMPT_TEST_ATTEMPTS; attemptCount++) {
|
|
48485
|
+
(_d = options.onAttemptStarted) === null || _d === void 0 ? void 0 : _d.call(options, attemptCount);
|
|
48486
|
+
const result = await options.runner.runPrompt({
|
|
48487
|
+
prompt: promptForCurrentAttempt,
|
|
48488
|
+
scriptPath: options.scriptPath,
|
|
48489
|
+
projectPath: options.projectPath,
|
|
48490
|
+
logPath: options.logPath,
|
|
48491
|
+
promptRoundArtifacts: options.promptRoundArtifacts,
|
|
48492
|
+
});
|
|
48493
|
+
console.info(colors.gray(`Running verification command after attempt #${attemptCount}: ${normalizedTestCommand}`));
|
|
48494
|
+
try {
|
|
48495
|
+
await runPromptTestCommandExecutor({
|
|
48496
|
+
command: normalizedTestCommand,
|
|
48497
|
+
projectPath: options.projectPath,
|
|
48498
|
+
scriptPath: buildPromptTestScriptPath(options.scriptPath),
|
|
48499
|
+
logPath: options.logPath,
|
|
48500
|
+
});
|
|
48501
|
+
return { ...result, attemptCount };
|
|
48502
|
+
}
|
|
48503
|
+
catch (error) {
|
|
48504
|
+
const fullVerificationOutput = formatUnknownErrorDetails(error);
|
|
48505
|
+
const feedbackVerificationOutput = limitVerificationOutputForFeedback(fullVerificationOutput);
|
|
48506
|
+
if (attemptCount >= MAX_PROMPT_TEST_ATTEMPTS) {
|
|
48507
|
+
console.error(colors.red(`Verification failed for ${options.promptLabel} after ${attemptCount} attempts.`));
|
|
48508
|
+
throw new Error(buildFinalVerificationFailureMessage({
|
|
48509
|
+
promptLabel: options.promptLabel,
|
|
48510
|
+
testCommand: normalizedTestCommand,
|
|
48511
|
+
attemptCount,
|
|
48512
|
+
verificationOutput: fullVerificationOutput,
|
|
48513
|
+
}));
|
|
48514
|
+
}
|
|
48515
|
+
console.warn(colors.yellow(`Verification failed for ${options.promptLabel} on attempt #${attemptCount}. Sending feedback to ${options.runner.name} and retrying...`));
|
|
48516
|
+
promptForCurrentAttempt = appendCoderContext(options.prompt, buildVerificationFeedback({
|
|
48517
|
+
testCommand: normalizedTestCommand,
|
|
48518
|
+
failedAttemptCount: attemptCount,
|
|
48519
|
+
verificationOutput: feedbackVerificationOutput,
|
|
48520
|
+
}));
|
|
48521
|
+
}
|
|
48522
|
+
}
|
|
48523
|
+
throw new Error('Unexpected prompt verification state.');
|
|
48524
|
+
}
|
|
48525
|
+
/**
|
|
48526
|
+
* Builds one feedback block appended to the next coding attempt after tests fail.
|
|
48527
|
+
*/
|
|
48528
|
+
function buildVerificationFeedback({ testCommand, failedAttemptCount, verificationOutput, }) {
|
|
48529
|
+
const nextAttemptCount = failedAttemptCount + 1;
|
|
48530
|
+
return spaceTrim((block) => `
|
|
48531
|
+
The previous implementation did not pass the required verification command.
|
|
48532
|
+
|
|
48533
|
+
## Automated verification feedback
|
|
48534
|
+
- Retry attempt: ${nextAttemptCount} of ${MAX_PROMPT_TEST_ATTEMPTS}
|
|
48535
|
+
- Verification command: \`${testCommand}\`
|
|
48536
|
+
- Update the current implementation so the verification command passes without breaking the original task requirements.
|
|
48537
|
+
|
|
48538
|
+
### Verification output
|
|
48539
|
+
\`\`\`
|
|
48540
|
+
${block(verificationOutput)}
|
|
48541
|
+
\`\`\`
|
|
48542
|
+
`);
|
|
48543
|
+
}
|
|
48544
|
+
/**
|
|
48545
|
+
* Builds the final error message written when verification still fails after all retries.
|
|
48546
|
+
*/
|
|
48547
|
+
function buildFinalVerificationFailureMessage({ promptLabel, testCommand, attemptCount, verificationOutput, }) {
|
|
48548
|
+
return spaceTrim((block) => `
|
|
48549
|
+
Verification command \`${testCommand}\` failed for \`${promptLabel}\` after ${attemptCount} attempts.
|
|
48550
|
+
|
|
48551
|
+
### Verification output
|
|
48552
|
+
\`\`\`
|
|
48553
|
+
${block(verificationOutput)}
|
|
48554
|
+
\`\`\`
|
|
48555
|
+
`);
|
|
48556
|
+
}
|
|
48557
|
+
/**
|
|
48558
|
+
* Limits verification output before it is embedded back into the next coding prompt.
|
|
48559
|
+
*/
|
|
48560
|
+
function limitVerificationOutputForFeedback(verificationOutput) {
|
|
48561
|
+
const normalizedVerificationOutput = verificationOutput.trim();
|
|
48562
|
+
if (normalizedVerificationOutput.length <= MAX_TEST_FEEDBACK_OUTPUT_CHARS) {
|
|
48563
|
+
return normalizedVerificationOutput;
|
|
48564
|
+
}
|
|
48565
|
+
return spaceTrim(`
|
|
48566
|
+
[...verification output truncated to the last ${MAX_TEST_FEEDBACK_OUTPUT_CHARS} characters...]
|
|
48567
|
+
${normalizedVerificationOutput.slice(-MAX_TEST_FEEDBACK_OUTPUT_CHARS)}
|
|
48568
|
+
`);
|
|
48569
|
+
}
|
|
48570
|
+
/**
|
|
48571
|
+
* Derives a dedicated temp-script path for verification commands.
|
|
48572
|
+
*/
|
|
48573
|
+
function buildPromptTestScriptPath(scriptPath) {
|
|
48574
|
+
if (scriptPath.toLowerCase().endsWith('.sh')) {
|
|
48575
|
+
return `${scriptPath.slice(0, -3)}.test.sh`;
|
|
48576
|
+
}
|
|
48577
|
+
return `${scriptPath}.test.sh`;
|
|
48578
|
+
}
|
|
48579
|
+
|
|
48580
|
+
/**
|
|
48581
|
+
* Maximum number of agent output lines kept in the scrolling output area.
|
|
48582
|
+
*
|
|
48583
|
+
* @private internal constant of coder run UI
|
|
48584
|
+
*/
|
|
48585
|
+
const MAX_AGENT_OUTPUT_LINES = 12;
|
|
48586
|
+
/**
|
|
48587
|
+
* Calendar formats used when displaying the estimated completion time.
|
|
48588
|
+
*
|
|
48589
|
+
* @private internal constant of coder run UI
|
|
48590
|
+
*/
|
|
48591
|
+
const ESTIMATED_DONE_CALENDAR_FORMATS = {
|
|
48592
|
+
sameDay: '[Today] h:mm',
|
|
48593
|
+
nextDay: '[Tomorrow] h:mm',
|
|
48594
|
+
nextWeek: 'dddd h:mm',
|
|
48595
|
+
lastDay: '[Yesterday] h:mm',
|
|
48596
|
+
lastWeek: 'dddd h:mm',
|
|
48597
|
+
sameElse: 'MMM D h:mm',
|
|
48598
|
+
};
|
|
48599
|
+
/**
|
|
48600
|
+
* Reactive state manager for the coder run terminal UI.
|
|
48601
|
+
*
|
|
48602
|
+
* Holds all data the Ink components need and emits `'change'` events
|
|
48603
|
+
* whenever any property is updated so the UI can re-render.
|
|
48604
|
+
*
|
|
48605
|
+
* @private internal utility of coder run UI
|
|
48606
|
+
*/
|
|
48607
|
+
class CoderRunUiState extends EventEmitter {
|
|
48608
|
+
constructor(startTime) {
|
|
48609
|
+
super();
|
|
48610
|
+
this.config = { agentName: '', priority: 0 };
|
|
48611
|
+
this.currentPromptLabel = '';
|
|
48612
|
+
this.currentAttempt = 1;
|
|
48613
|
+
this.maxAttempts = 3;
|
|
48614
|
+
this.agentOutputLines = [];
|
|
48615
|
+
this.phase = 'initializing';
|
|
48616
|
+
this.statusMessage = 'Initializing...';
|
|
48617
|
+
this.errors = [];
|
|
48618
|
+
this.stats = { done: 0, forAgent: 0, belowMinimumPriority: 0, toBeWritten: 0 };
|
|
48619
|
+
/**
|
|
48620
|
+
* Total milliseconds the timer was paused/waiting (excluded from elapsed display).
|
|
48621
|
+
*/
|
|
48622
|
+
this.pausedMs = 0;
|
|
48623
|
+
this.startTime = startTime;
|
|
48624
|
+
// Timer starts paused — callers call `resumeTimer()` when actual work begins.
|
|
48625
|
+
this.pausedSince = startTime.clone();
|
|
48626
|
+
}
|
|
48627
|
+
/**
|
|
48628
|
+
* Pauses the elapsed timer (e.g. while waiting for user input or paused state).
|
|
48629
|
+
*/
|
|
48630
|
+
pauseTimer() {
|
|
48631
|
+
if (this.pausedSince === undefined) {
|
|
48632
|
+
this.pausedSince = moment();
|
|
48633
|
+
}
|
|
48634
|
+
}
|
|
48635
|
+
/**
|
|
48636
|
+
* Resumes the elapsed timer after a pause.
|
|
48637
|
+
*/
|
|
48638
|
+
resumeTimer() {
|
|
48639
|
+
if (this.pausedSince !== undefined) {
|
|
48640
|
+
this.pausedMs += moment().diff(this.pausedSince);
|
|
48641
|
+
this.pausedSince = undefined;
|
|
48642
|
+
}
|
|
48643
|
+
}
|
|
48644
|
+
/**
|
|
48645
|
+
* Replaces the configuration shown in the UI header.
|
|
48646
|
+
*/
|
|
48647
|
+
setConfig(config) {
|
|
48648
|
+
this.config = config;
|
|
48649
|
+
this.emitChange();
|
|
48650
|
+
}
|
|
48651
|
+
/**
|
|
48652
|
+
* Feeds new prompt statistics from the main loop so the progress bar updates.
|
|
48653
|
+
*/
|
|
48654
|
+
updateProgress(stats) {
|
|
48655
|
+
if (this.initialDone === undefined && (stats.done > 0 || stats.forAgent > 0 || stats.toBeWritten > 0)) {
|
|
48656
|
+
this.initialDone = stats.done;
|
|
48657
|
+
}
|
|
48658
|
+
this.stats = stats;
|
|
48659
|
+
this.emitChange();
|
|
48660
|
+
}
|
|
48661
|
+
/**
|
|
48662
|
+
* Computes a progress snapshot on demand so elapsed time ticks with periodic re-renders.
|
|
48663
|
+
*/
|
|
48664
|
+
getProgress() {
|
|
48665
|
+
var _a;
|
|
48666
|
+
const stats = this.stats;
|
|
48667
|
+
const totalPrompts = stats.done + stats.forAgent + stats.toBeWritten;
|
|
48668
|
+
const sessionDone = Math.max(0, stats.done - ((_a = this.initialDone) !== null && _a !== void 0 ? _a : stats.done));
|
|
48669
|
+
const sessionTotal = sessionDone + stats.forAgent;
|
|
48670
|
+
const percentage = totalPrompts > 0 ? Math.round((stats.done / totalPrompts) * 100) : 0;
|
|
48671
|
+
const wallMs = moment().diff(this.startTime);
|
|
48672
|
+
const currentPauseMs = this.pausedSince !== undefined ? moment().diff(this.pausedSince) : 0;
|
|
48673
|
+
const activeMs = Math.max(0, wallMs - this.pausedMs - currentPauseMs);
|
|
48674
|
+
const elapsedDuration = moment.duration(activeMs);
|
|
48675
|
+
const elapsedText = formatDurationBrief(elapsedDuration);
|
|
48676
|
+
let estimatedTotalText = '\u2014';
|
|
48677
|
+
let estimatedLabel = 'unknown';
|
|
48678
|
+
if (totalPrompts > 0 && stats.done > 0) {
|
|
48679
|
+
const estimatedTotalMs = (elapsedDuration.asMilliseconds() * totalPrompts) / stats.done;
|
|
48680
|
+
const estimatedRemainingMs = estimatedTotalMs - elapsedDuration.asMilliseconds();
|
|
48681
|
+
const estimatedTotalDuration = moment.duration(estimatedTotalMs);
|
|
48682
|
+
const estimatedCompletion = moment().add(estimatedRemainingMs, 'milliseconds');
|
|
48683
|
+
estimatedTotalText = formatDurationBrief(estimatedTotalDuration);
|
|
48684
|
+
estimatedLabel = estimatedCompletion.calendar(null, ESTIMATED_DONE_CALENDAR_FORMATS);
|
|
48685
|
+
}
|
|
48686
|
+
return {
|
|
48687
|
+
totalPrompts,
|
|
48688
|
+
sessionDone,
|
|
48689
|
+
sessionTotal,
|
|
48690
|
+
percentage,
|
|
48691
|
+
elapsedText,
|
|
48692
|
+
estimatedTotalText,
|
|
48693
|
+
estimatedLabel,
|
|
48694
|
+
};
|
|
48695
|
+
}
|
|
48696
|
+
/**
|
|
48697
|
+
* Sets the label of the prompt currently being processed and resets per-prompt state.
|
|
48698
|
+
*/
|
|
48699
|
+
setCurrentPrompt(label) {
|
|
48700
|
+
this.currentPromptLabel = label;
|
|
48701
|
+
this.agentOutputLines = [];
|
|
48702
|
+
this.currentAttempt = 1;
|
|
48703
|
+
this.emitChange();
|
|
48704
|
+
}
|
|
48705
|
+
/**
|
|
48706
|
+
* Updates the current retry attempt number.
|
|
48707
|
+
*/
|
|
48708
|
+
setAttempt(attempt) {
|
|
48709
|
+
this.currentAttempt = attempt;
|
|
48710
|
+
this.emitChange();
|
|
48711
|
+
}
|
|
48712
|
+
/**
|
|
48713
|
+
* Appends raw agent output text, keeping only the last `MAX_AGENT_OUTPUT_LINES`.
|
|
48714
|
+
*/
|
|
48715
|
+
addAgentOutput(text) {
|
|
48716
|
+
const lines = text.split(/\r?\n/).filter((line) => line.trim() !== '');
|
|
48717
|
+
if (lines.length === 0) {
|
|
48718
|
+
return;
|
|
48719
|
+
}
|
|
48720
|
+
this.agentOutputLines.push(...lines);
|
|
48721
|
+
if (this.agentOutputLines.length > MAX_AGENT_OUTPUT_LINES) {
|
|
48722
|
+
this.agentOutputLines = this.agentOutputLines.slice(-MAX_AGENT_OUTPUT_LINES);
|
|
48723
|
+
}
|
|
48724
|
+
this.emitChange();
|
|
48725
|
+
}
|
|
48726
|
+
/**
|
|
48727
|
+
* Transitions the execution phase shown in the UI.
|
|
48728
|
+
*/
|
|
48729
|
+
setPhase(phase) {
|
|
48730
|
+
this.phase = phase;
|
|
48731
|
+
this.emitChange();
|
|
48732
|
+
}
|
|
48733
|
+
/**
|
|
48734
|
+
* Updates the status message line beneath the current prompt label.
|
|
48735
|
+
*/
|
|
48736
|
+
setStatusMessage(message) {
|
|
48737
|
+
this.statusMessage = message;
|
|
48738
|
+
this.emitChange();
|
|
48739
|
+
}
|
|
48740
|
+
/**
|
|
48741
|
+
* Appends an error message to the error list shown in the UI.
|
|
48742
|
+
*/
|
|
48743
|
+
addError(errorMessage) {
|
|
48744
|
+
this.errors.push(errorMessage);
|
|
48745
|
+
this.emitChange();
|
|
48746
|
+
}
|
|
48747
|
+
emitChange() {
|
|
48748
|
+
this.emit('change');
|
|
48749
|
+
}
|
|
48750
|
+
}
|
|
48751
|
+
/**
|
|
48752
|
+
* Formats a duration into a compact string such as "3h 12m" or "45s".
|
|
48753
|
+
*
|
|
48754
|
+
* @private internal utility of coder run UI
|
|
48755
|
+
*/
|
|
48756
|
+
function formatDurationBrief(duration) {
|
|
48757
|
+
const totalSeconds = Math.max(0, Math.round(duration.asSeconds()));
|
|
48758
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
48759
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
48760
|
+
const seconds = totalSeconds % 60;
|
|
48761
|
+
const parts = [];
|
|
48762
|
+
if (hours > 0) {
|
|
48763
|
+
parts.push(`${hours}h`);
|
|
48764
|
+
}
|
|
48765
|
+
if (minutes > 0) {
|
|
48766
|
+
parts.push(`${minutes}m`);
|
|
48767
|
+
}
|
|
48768
|
+
if (!parts.length && seconds > 0) {
|
|
48769
|
+
parts.push(`${seconds}s`);
|
|
48770
|
+
}
|
|
48771
|
+
if (!parts.length) {
|
|
48772
|
+
parts.push('0s');
|
|
48773
|
+
}
|
|
48774
|
+
return parts.join(' ');
|
|
48775
|
+
}
|
|
48776
|
+
|
|
48777
|
+
/**
|
|
48778
|
+
* Refresh interval for the terminal UI in milliseconds.
|
|
48779
|
+
*
|
|
48780
|
+
* @private internal constant of coder run UI
|
|
48781
|
+
*/
|
|
48782
|
+
const UI_REFRESH_INTERVAL_MS = 200;
|
|
48783
|
+
/**
|
|
48784
|
+
* Character width used for the text progress bar.
|
|
48785
|
+
*
|
|
48786
|
+
* @private internal constant of coder run UI
|
|
48787
|
+
*/
|
|
48788
|
+
const PROGRESS_BAR_WIDTH = 40;
|
|
48789
|
+
/**
|
|
48790
|
+
* Maximum number of output lines reserved for agent output in the UI.
|
|
48791
|
+
*
|
|
48792
|
+
* @private internal constant of coder run UI
|
|
48793
|
+
*/
|
|
48794
|
+
const MAX_VISIBLE_OUTPUT_LINES = 8;
|
|
48795
|
+
/**
|
|
48796
|
+
* Spinner animation frames.
|
|
48797
|
+
*
|
|
48798
|
+
* @private internal constant of coder run UI
|
|
48799
|
+
*/
|
|
48800
|
+
const SPINNER_FRAMES = [
|
|
48801
|
+
'\u280B',
|
|
48802
|
+
'\u2819',
|
|
48803
|
+
'\u2839',
|
|
48804
|
+
'\u2838',
|
|
48805
|
+
'\u283C',
|
|
48806
|
+
'\u2834',
|
|
48807
|
+
'\u2826',
|
|
48808
|
+
'\u2827',
|
|
48809
|
+
'\u2807',
|
|
48810
|
+
'\u280F',
|
|
48811
|
+
];
|
|
48812
|
+
/**
|
|
48813
|
+
* Strips ANSI escape codes from a string.
|
|
48814
|
+
*
|
|
48815
|
+
* @private internal utility of coder run UI
|
|
48816
|
+
*/
|
|
48817
|
+
function stripAnsi(text) {
|
|
48818
|
+
// eslint-disable-next-line no-control-regex
|
|
48819
|
+
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
48820
|
+
}
|
|
48821
|
+
/**
|
|
48822
|
+
* Returns the usable terminal width, capped at 80.
|
|
48823
|
+
*
|
|
48824
|
+
* @private internal utility of coder run UI
|
|
48825
|
+
*/
|
|
48826
|
+
function getTerminalWidth() {
|
|
48827
|
+
return Math.min(process.stdout.columns || 80, 80);
|
|
48828
|
+
}
|
|
48829
|
+
/**
|
|
48830
|
+
* Builds a text progress bar string from a percentage.
|
|
48831
|
+
*
|
|
48832
|
+
* @private internal utility of coder run UI
|
|
48833
|
+
*/
|
|
48834
|
+
function buildProgressBar(percentage) {
|
|
48835
|
+
const filled = Math.round((percentage / 100) * PROGRESS_BAR_WIDTH);
|
|
48836
|
+
const empty = PROGRESS_BAR_WIDTH - filled;
|
|
48837
|
+
return colors.green('\u2588'.repeat(filled)) + colors.gray('\u2591'.repeat(empty)) + ` ${percentage}%`;
|
|
48838
|
+
}
|
|
48839
|
+
/**
|
|
48840
|
+
* Boots the ANSI terminal UI for `ptbk coder run`.
|
|
48841
|
+
*
|
|
48842
|
+
* The UI reserves a fixed number of terminal lines and repaints them periodically.
|
|
48843
|
+
* Between repaints, any console output from runners is captured and fed into the
|
|
48844
|
+
* scrolling agent-output area.
|
|
48845
|
+
*
|
|
48846
|
+
* On non-interactive (non-TTY) terminals the UI is skipped entirely and
|
|
48847
|
+
* only the state object is provided.
|
|
48848
|
+
*
|
|
48849
|
+
* @private internal entry point of coder run UI
|
|
48850
|
+
*/
|
|
48851
|
+
function renderCoderRunUi(startTime) {
|
|
48852
|
+
const state = new CoderRunUiState(startTime);
|
|
48853
|
+
if (!process.stdout.isTTY) {
|
|
48854
|
+
return {
|
|
48855
|
+
state,
|
|
48856
|
+
startCapturingAgentOutput: () => { },
|
|
48857
|
+
stopCapturingAgentOutput: () => { },
|
|
48858
|
+
cleanup: () => { },
|
|
48859
|
+
};
|
|
48860
|
+
}
|
|
48861
|
+
// --- Console interception ---
|
|
48862
|
+
const originalConsoleInfo = console.info;
|
|
48863
|
+
const originalConsoleWarn = console.warn;
|
|
48864
|
+
const originalConsoleError = console.error;
|
|
48865
|
+
const originalConsoleLog = console.log;
|
|
48866
|
+
let isCapturing = false;
|
|
48867
|
+
console.info = (...args) => {
|
|
48868
|
+
if (isCapturing) {
|
|
48869
|
+
state.addAgentOutput(args.map(String).join(' '));
|
|
48870
|
+
}
|
|
48871
|
+
// In UI mode, non-captured output is intentionally suppressed
|
|
48872
|
+
// so it does not interfere with the repainted frame.
|
|
48873
|
+
};
|
|
48874
|
+
console.warn = (...args) => {
|
|
48875
|
+
if (isCapturing) {
|
|
48876
|
+
state.addAgentOutput(args.map(String).join(' '));
|
|
48877
|
+
}
|
|
48878
|
+
};
|
|
48879
|
+
console.error = (...args) => {
|
|
48880
|
+
if (isCapturing) {
|
|
48881
|
+
state.addError(args.map(String).join(' '));
|
|
48882
|
+
}
|
|
48883
|
+
};
|
|
48884
|
+
console.log = (...args) => {
|
|
48885
|
+
if (isCapturing) {
|
|
48886
|
+
state.addAgentOutput(args.map(String).join(' '));
|
|
48887
|
+
}
|
|
48888
|
+
};
|
|
48889
|
+
// --- Keyboard input (pause) ---
|
|
48890
|
+
const readline = require('readline');
|
|
48891
|
+
readline.emitKeypressEvents(process.stdin);
|
|
48892
|
+
if (process.stdin.isTTY) {
|
|
48893
|
+
process.stdin.setRawMode(true);
|
|
48894
|
+
}
|
|
48895
|
+
const keypressHandler = (_str, key) => {
|
|
48896
|
+
if (key.ctrl && key.name === 'c') {
|
|
48897
|
+
cleanup();
|
|
48898
|
+
process.exit(0);
|
|
48899
|
+
}
|
|
48900
|
+
if (key.name === 'p') {
|
|
48901
|
+
const current = getPauseState();
|
|
48902
|
+
if (current === 'RUNNING') {
|
|
48903
|
+
requestPause();
|
|
48904
|
+
}
|
|
48905
|
+
else {
|
|
48906
|
+
requestResume();
|
|
48907
|
+
}
|
|
48908
|
+
}
|
|
48909
|
+
};
|
|
48910
|
+
process.stdin.on('keypress', keypressHandler);
|
|
48911
|
+
// --- Rendering ---
|
|
48912
|
+
let spinnerFrame = 0;
|
|
48913
|
+
let previousFrameLineCount = 0;
|
|
48914
|
+
let isRendering = false;
|
|
48915
|
+
let renderScheduled = false;
|
|
48916
|
+
/**
|
|
48917
|
+
* Schedules a render on the next tick if one isn't already pending.
|
|
48918
|
+
* Prevents overlapping renders that cause cursor desync.
|
|
48919
|
+
*/
|
|
48920
|
+
function scheduleRender() {
|
|
48921
|
+
if (renderScheduled) {
|
|
48922
|
+
return;
|
|
48923
|
+
}
|
|
48924
|
+
renderScheduled = true;
|
|
48925
|
+
setImmediate(() => {
|
|
48926
|
+
renderScheduled = false;
|
|
48927
|
+
render();
|
|
48928
|
+
});
|
|
48929
|
+
}
|
|
48930
|
+
/**
|
|
48931
|
+
* Clears previously rendered lines and writes a new frame.
|
|
48932
|
+
*/
|
|
48933
|
+
function render() {
|
|
48934
|
+
if (isRendering) {
|
|
48935
|
+
return;
|
|
48936
|
+
}
|
|
48937
|
+
isRendering = true;
|
|
48938
|
+
try {
|
|
48939
|
+
const lines = buildFrame();
|
|
48940
|
+
// Move cursor up to clear the previous frame.
|
|
48941
|
+
if (previousFrameLineCount > 0) {
|
|
48942
|
+
process.stdout.write(`\x1b[${previousFrameLineCount}A`);
|
|
48943
|
+
}
|
|
48944
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48945
|
+
clearLine(process.stdout, 0);
|
|
48946
|
+
cursorTo(process.stdout, 0);
|
|
48947
|
+
process.stdout.write(lines[i]);
|
|
48948
|
+
if (i < lines.length - 1) {
|
|
48949
|
+
process.stdout.write('\n');
|
|
48950
|
+
}
|
|
48951
|
+
}
|
|
48952
|
+
// Clear any leftover lines from a previous longer frame.
|
|
48953
|
+
if (lines.length < previousFrameLineCount) {
|
|
48954
|
+
for (let i = lines.length; i < previousFrameLineCount; i++) {
|
|
48955
|
+
process.stdout.write('\n');
|
|
48956
|
+
clearLine(process.stdout, 0);
|
|
48957
|
+
cursorTo(process.stdout, 0);
|
|
48958
|
+
}
|
|
48959
|
+
// Move back up to the end of the current frame.
|
|
48960
|
+
const overshoot = previousFrameLineCount - lines.length;
|
|
48961
|
+
if (overshoot > 0) {
|
|
48962
|
+
process.stdout.write(`\x1b[${overshoot}A`);
|
|
48963
|
+
}
|
|
48964
|
+
}
|
|
48965
|
+
previousFrameLineCount = lines.length;
|
|
48966
|
+
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
48967
|
+
}
|
|
48968
|
+
finally {
|
|
48969
|
+
isRendering = false;
|
|
48970
|
+
}
|
|
48971
|
+
}
|
|
48972
|
+
/**
|
|
48973
|
+
* Builds the complete frame as an array of terminal lines.
|
|
48974
|
+
*/
|
|
48975
|
+
function buildFrame() {
|
|
48976
|
+
const w = getTerminalWidth();
|
|
48977
|
+
const sep = colors.gray('\u2500'.repeat(w - 2));
|
|
48978
|
+
const spinner = SPINNER_FRAMES[spinnerFrame];
|
|
48979
|
+
const { config, phase, currentPromptLabel, currentAttempt, maxAttempts, statusMessage, agentOutputLines, errors, } = state;
|
|
48980
|
+
const progress = state.getProgress();
|
|
48981
|
+
const isPaused = getPauseState() !== 'RUNNING';
|
|
48982
|
+
const isActive = phase === 'running' || phase === 'verifying' || phase === 'loading';
|
|
48983
|
+
const lines = [];
|
|
48984
|
+
// --- Branding ---
|
|
48985
|
+
lines.push(colors.bold.cyan('\u2728 Promptbook Coder'));
|
|
48986
|
+
// --- Config ---
|
|
48987
|
+
let configLine1 = `Agent: ${colors.bold.green(config.agentName)}`;
|
|
48988
|
+
if (config.modelName) {
|
|
48989
|
+
configLine1 += ` \u2502 Model: ${colors.bold(config.modelName)}`;
|
|
48990
|
+
}
|
|
48991
|
+
if (config.thinkingLevel) {
|
|
48992
|
+
configLine1 += ` \u2502 Thinking: ${colors.bold(config.thinkingLevel)}`;
|
|
48993
|
+
}
|
|
48994
|
+
lines.push(configLine1);
|
|
48995
|
+
let configLine2 = '';
|
|
48996
|
+
if (config.context) {
|
|
48997
|
+
configLine2 += `Context: ${colors.yellow(config.context)} \u2502 `;
|
|
48998
|
+
}
|
|
48999
|
+
configLine2 += `Priority: \u2265${config.priority}`;
|
|
49000
|
+
if (config.testCommand) {
|
|
49001
|
+
configLine2 += ` \u2502 Test: ${colors.gray(config.testCommand)}`;
|
|
49002
|
+
}
|
|
49003
|
+
lines.push(configLine2);
|
|
49004
|
+
// --- Separator ---
|
|
49005
|
+
lines.push(sep);
|
|
49006
|
+
// --- Progress ---
|
|
49007
|
+
const progressSummary = [
|
|
49008
|
+
`${progress.sessionDone}/${progress.sessionTotal} Prompts (${progress.totalPrompts} total)`,
|
|
49009
|
+
`${progress.elapsedText}/${progress.estimatedTotalText}`,
|
|
49010
|
+
`Est. done ${progress.estimatedLabel}`,
|
|
49011
|
+
].join(' \u2502 ');
|
|
49012
|
+
lines.push(progressSummary);
|
|
49013
|
+
lines.push(buildProgressBar(progress.percentage));
|
|
49014
|
+
// --- Separator ---
|
|
49015
|
+
lines.push(sep);
|
|
49016
|
+
// --- Current prompt ---
|
|
49017
|
+
if (currentPromptLabel) {
|
|
49018
|
+
const spinnerPrefix = isActive ? colors.yellow(`${spinner} `) : ' ';
|
|
49019
|
+
lines.push(spinnerPrefix + colors.bold(currentPromptLabel));
|
|
49020
|
+
lines.push(colors.gray(`Attempt ${currentAttempt}/${maxAttempts} \u2502 ${statusMessage}`));
|
|
49021
|
+
}
|
|
49022
|
+
else {
|
|
49023
|
+
lines.push(colors.gray(statusMessage));
|
|
49024
|
+
}
|
|
49025
|
+
// --- Agent output ---
|
|
49026
|
+
if (agentOutputLines.length > 0) {
|
|
49027
|
+
lines.push('');
|
|
49028
|
+
lines.push(colors.gray.bold('Agent output:'));
|
|
49029
|
+
const visibleLines = agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES);
|
|
49030
|
+
for (const line of visibleLines) {
|
|
49031
|
+
const cleanLine = stripAnsi(line);
|
|
49032
|
+
// Truncate to terminal width.
|
|
49033
|
+
const truncated = cleanLine.length > w - 2 ? cleanLine.slice(0, w - 5) + '...' : cleanLine;
|
|
49034
|
+
lines.push(colors.gray(truncated));
|
|
49035
|
+
}
|
|
49036
|
+
}
|
|
49037
|
+
// --- Errors ---
|
|
49038
|
+
if (errors.length > 0) {
|
|
49039
|
+
lines.push('');
|
|
49040
|
+
for (const err of errors) {
|
|
49041
|
+
lines.push(colors.red(`\u2717 ${err}`));
|
|
49042
|
+
}
|
|
49043
|
+
}
|
|
49044
|
+
// --- Separator ---
|
|
49045
|
+
lines.push(sep);
|
|
49046
|
+
// --- Controls ---
|
|
49047
|
+
const pauseLabel = isPaused
|
|
49048
|
+
? colors.bgYellow.black(' PAUSED ') + colors.gray(' [P] Resume \u2502 Ctrl+C Exit')
|
|
49049
|
+
: colors.gray('[P] Pause \u2502 Ctrl+C Exit');
|
|
49050
|
+
lines.push(pauseLabel);
|
|
49051
|
+
return lines;
|
|
49052
|
+
}
|
|
49053
|
+
// Initial render.
|
|
49054
|
+
process.stdout.write('\n');
|
|
49055
|
+
render();
|
|
49056
|
+
const interval = setInterval(scheduleRender, UI_REFRESH_INTERVAL_MS);
|
|
49057
|
+
// Listen for state changes and schedule a re-render (debounced).
|
|
49058
|
+
state.on('change', scheduleRender);
|
|
49059
|
+
// --- Cleanup ---
|
|
49060
|
+
function cleanup() {
|
|
49061
|
+
clearInterval(interval);
|
|
49062
|
+
state.off('change', scheduleRender);
|
|
49063
|
+
process.stdin.off('keypress', keypressHandler);
|
|
49064
|
+
if (process.stdin.isTTY) {
|
|
49065
|
+
process.stdin.setRawMode(false);
|
|
49066
|
+
}
|
|
49067
|
+
isCapturing = false;
|
|
49068
|
+
console.info = originalConsoleInfo;
|
|
49069
|
+
console.warn = originalConsoleWarn;
|
|
49070
|
+
console.error = originalConsoleError;
|
|
49071
|
+
console.log = originalConsoleLog;
|
|
49072
|
+
// Render one final frame so the user sees the last state.
|
|
49073
|
+
render();
|
|
49074
|
+
process.stdout.write('\n');
|
|
49075
|
+
}
|
|
49076
|
+
return {
|
|
49077
|
+
state,
|
|
49078
|
+
startCapturingAgentOutput() {
|
|
49079
|
+
isCapturing = true;
|
|
49080
|
+
},
|
|
49081
|
+
stopCapturingAgentOutput() {
|
|
49082
|
+
isCapturing = false;
|
|
49083
|
+
},
|
|
49084
|
+
cleanup,
|
|
49085
|
+
};
|
|
49086
|
+
}
|
|
49087
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
49088
|
+
|
|
47899
49089
|
/**
|
|
47900
49090
|
* Constant for prompts dir.
|
|
47901
49091
|
*/
|
|
@@ -47958,8 +49148,13 @@ async function runCodexPrompts(providedOptions) {
|
|
|
47958
49148
|
`));
|
|
47959
49149
|
}
|
|
47960
49150
|
const runStartDate = moment();
|
|
47961
|
-
const
|
|
47962
|
-
|
|
49151
|
+
const isUiMode = !options.dryRun && Boolean(process.stdout.isTTY);
|
|
49152
|
+
const progressDisplay = options.dryRun || isUiMode ? undefined : new CliProgressDisplay(runStartDate);
|
|
49153
|
+
const uiHandle = isUiMode ? renderCoderRunUi(runStartDate) : undefined;
|
|
49154
|
+
// When the Ink UI is active it handles keyboard input itself, so skip the raw stdin listener.
|
|
49155
|
+
if (!isUiMode) {
|
|
49156
|
+
listenForPause();
|
|
49157
|
+
}
|
|
47963
49158
|
try {
|
|
47964
49159
|
const resolvedCoderContext = await resolveCoderContext(options.context, process.cwd());
|
|
47965
49160
|
if (options.dryRun) {
|
|
@@ -48058,35 +49253,78 @@ async function runCodexPrompts(providedOptions) {
|
|
|
48058
49253
|
}
|
|
48059
49254
|
console.info(colors.green(`Running prompts with ${runner.name}`));
|
|
48060
49255
|
const runnerMetadata = getRunnerMetadata(options, actualRunnerModel);
|
|
49256
|
+
// Feed configuration into the terminal UI
|
|
49257
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setConfig({
|
|
49258
|
+
agentName: runner.name,
|
|
49259
|
+
modelName: actualRunnerModel,
|
|
49260
|
+
thinkingLevel: options.thinkingLevel,
|
|
49261
|
+
context: options.context,
|
|
49262
|
+
priority: options.priority,
|
|
49263
|
+
testCommand: options.testCommand,
|
|
49264
|
+
});
|
|
49265
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
49266
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Running prompts with ${runner.name}`);
|
|
48061
49267
|
let hasShownUpcomingTasks = false;
|
|
48062
49268
|
let hasWaitedForStart = false;
|
|
48063
49269
|
while (just(true)) {
|
|
48064
|
-
await checkPause(
|
|
49270
|
+
await checkPause({
|
|
49271
|
+
silent: isUiMode,
|
|
49272
|
+
onPaused: () => {
|
|
49273
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49274
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
|
|
49275
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
|
|
49276
|
+
},
|
|
49277
|
+
onResumed: () => {
|
|
49278
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
49279
|
+
},
|
|
49280
|
+
});
|
|
49281
|
+
if (isUiMode) {
|
|
49282
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
49283
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Loading prompts...');
|
|
49284
|
+
}
|
|
48065
49285
|
const promptFiles = await loadPromptFiles(PROMPTS_DIR$1);
|
|
48066
49286
|
const stats = summarizePrompts(promptFiles, options.priority);
|
|
48067
49287
|
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.update(stats);
|
|
48068
|
-
|
|
49288
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.updateProgress(stats);
|
|
49289
|
+
if (!isUiMode) {
|
|
49290
|
+
printStats(stats, options.priority);
|
|
49291
|
+
}
|
|
48069
49292
|
const nextPrompt = findNextTodoPrompt(promptFiles, options.priority);
|
|
48070
49293
|
if (!hasShownUpcomingTasks) {
|
|
48071
|
-
if (stats.toBeWritten > 0) {
|
|
49294
|
+
if (stats.toBeWritten > 0 && !isUiMode) {
|
|
48072
49295
|
console.info(colors.yellow('Following prompts need to be written:'));
|
|
48073
49296
|
printPromptsToBeWritten(promptFiles, options.priority);
|
|
48074
49297
|
console.info('');
|
|
48075
49298
|
}
|
|
48076
|
-
|
|
49299
|
+
if (!isUiMode) {
|
|
49300
|
+
printUpcomingTasks(listUpcomingTasks(promptFiles, options.priority));
|
|
49301
|
+
}
|
|
48077
49302
|
hasShownUpcomingTasks = true;
|
|
48078
49303
|
}
|
|
48079
49304
|
if (!nextPrompt) {
|
|
48080
49305
|
if (stats.toBeWritten > 0) {
|
|
48081
|
-
|
|
49306
|
+
const message = 'No prompts ready for agent.';
|
|
49307
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
|
|
49308
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('done');
|
|
49309
|
+
if (!isUiMode) {
|
|
49310
|
+
console.info(colors.yellow(message));
|
|
49311
|
+
}
|
|
48082
49312
|
}
|
|
48083
49313
|
else {
|
|
48084
|
-
|
|
49314
|
+
const message = 'All prompts are done.';
|
|
49315
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(message);
|
|
49316
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('done');
|
|
49317
|
+
if (!isUiMode) {
|
|
49318
|
+
console.info(colors.green(message));
|
|
49319
|
+
}
|
|
48085
49320
|
}
|
|
48086
49321
|
return;
|
|
48087
49322
|
}
|
|
48088
49323
|
if (options.waitForUser) {
|
|
49324
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49325
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(hasWaitedForStart ? 'Waiting... Press Enter to continue' : 'Waiting... Press Enter to start');
|
|
48089
49326
|
await waitForPromptStart(nextPrompt.file, nextPrompt.section, !hasWaitedForStart);
|
|
49327
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
48090
49328
|
hasWaitedForStart = true;
|
|
48091
49329
|
}
|
|
48092
49330
|
if (!options.ignoreGitChanges) {
|
|
@@ -48096,44 +49334,85 @@ async function runCodexPrompts(providedOptions) {
|
|
|
48096
49334
|
const codexPrompt = appendCoderContext(buildCodexPrompt(nextPrompt.file, nextPrompt.section), resolvedCoderContext);
|
|
48097
49335
|
const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
|
|
48098
49336
|
const promptLabel = buildPromptLabelForDisplay(nextPrompt.file, nextPrompt.section);
|
|
48099
|
-
|
|
49337
|
+
const promptRoundArtifacts = createCoderRunPromptRoundArtifacts(options.preserveLogs);
|
|
49338
|
+
if (isUiMode) {
|
|
49339
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
|
|
49340
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
|
|
49341
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Running');
|
|
49342
|
+
}
|
|
49343
|
+
else {
|
|
49344
|
+
console.info(colors.blue(`Processing ${promptLabel}`));
|
|
49345
|
+
}
|
|
48100
49346
|
const promptExecutionStartedDate = moment();
|
|
49347
|
+
let attemptCount = 1;
|
|
49348
|
+
let promptRoundOutcome = 'failure';
|
|
48101
49349
|
const roundChangedFilesSnapshot = options.normalizeLineEndings
|
|
48102
49350
|
? await captureChangedFilesSnapshot(process.cwd())
|
|
48103
49351
|
: undefined;
|
|
48104
49352
|
try {
|
|
48105
|
-
|
|
48106
|
-
|
|
48107
|
-
|
|
48108
|
-
|
|
48109
|
-
|
|
48110
|
-
|
|
48111
|
-
|
|
48112
|
-
|
|
48113
|
-
|
|
48114
|
-
|
|
48115
|
-
|
|
48116
|
-
|
|
48117
|
-
|
|
48118
|
-
|
|
49353
|
+
await withPromptRuntimeLog(scriptPath, async (logPath) => {
|
|
49354
|
+
try {
|
|
49355
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
|
|
49356
|
+
const result = await runPromptWithTestFeedback({
|
|
49357
|
+
runner,
|
|
49358
|
+
prompt: codexPrompt,
|
|
49359
|
+
scriptPath,
|
|
49360
|
+
projectPath: process.cwd(),
|
|
49361
|
+
promptLabel,
|
|
49362
|
+
testCommand: options.testCommand,
|
|
49363
|
+
logPath,
|
|
49364
|
+
promptRoundArtifacts,
|
|
49365
|
+
onAttemptStarted: (nextAttemptCount) => {
|
|
49366
|
+
attemptCount = nextAttemptCount;
|
|
49367
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(nextAttemptCount);
|
|
49368
|
+
if (nextAttemptCount > 1) {
|
|
49369
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage(`Retrying (attempt ${nextAttemptCount})`);
|
|
49370
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('verifying');
|
|
49371
|
+
}
|
|
49372
|
+
},
|
|
49373
|
+
});
|
|
49374
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
49375
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Committing changes');
|
|
49376
|
+
markPromptDone(nextPrompt.file, nextPrompt.section, result.usage, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, result.attemptCount);
|
|
49377
|
+
await writePromptFile(nextPrompt.file);
|
|
49378
|
+
await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
|
|
49379
|
+
if (options.waitForUser) {
|
|
49380
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49381
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Waiting... Press Enter to commit');
|
|
49382
|
+
printCommitMessage(commitMessage);
|
|
49383
|
+
await waitForEnter(colors.bgWhite('Press Enter to commit and continue...'));
|
|
49384
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
49385
|
+
}
|
|
49386
|
+
await commitChanges(commitMessage, { autoPush: options.autoPush });
|
|
49387
|
+
await runPostPromptAutoMigrationIfEnabled(options);
|
|
49388
|
+
promptRoundOutcome = 'success';
|
|
49389
|
+
}
|
|
49390
|
+
catch (error) {
|
|
49391
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
49392
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('error');
|
|
49393
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.addError(error instanceof Error ? error.message : String(error));
|
|
49394
|
+
markPromptFailed(nextPrompt.file, nextPrompt.section, runnerMetadata.runnerName, runnerMetadata.modelName, promptExecutionStartedDate, attemptCount);
|
|
49395
|
+
await writePromptFile(nextPrompt.file);
|
|
49396
|
+
await writePromptErrorLog({
|
|
49397
|
+
file: nextPrompt.file,
|
|
49398
|
+
section: nextPrompt.section,
|
|
49399
|
+
runnerName: runnerMetadata.runnerName,
|
|
49400
|
+
modelName: runnerMetadata.modelName,
|
|
49401
|
+
error,
|
|
49402
|
+
});
|
|
49403
|
+
await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
|
|
49404
|
+
throw error;
|
|
49405
|
+
}
|
|
49406
|
+
}, promptRoundArtifacts);
|
|
48119
49407
|
}
|
|
48120
|
-
|
|
48121
|
-
|
|
48122
|
-
await writePromptFile(nextPrompt.file);
|
|
48123
|
-
await writePromptErrorLog({
|
|
48124
|
-
file: nextPrompt.file,
|
|
48125
|
-
section: nextPrompt.section,
|
|
48126
|
-
runnerName: runnerMetadata.runnerName,
|
|
48127
|
-
modelName: runnerMetadata.modelName,
|
|
48128
|
-
error,
|
|
48129
|
-
});
|
|
48130
|
-
await normalizeLineEndingsForCurrentRound(options, roundChangedFilesSnapshot);
|
|
48131
|
-
throw error;
|
|
49408
|
+
finally {
|
|
49409
|
+
await promptRoundArtifacts.cleanup(promptRoundOutcome);
|
|
48132
49410
|
}
|
|
48133
49411
|
}
|
|
48134
49412
|
}
|
|
48135
49413
|
finally {
|
|
48136
49414
|
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.stop();
|
|
49415
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.cleanup();
|
|
48137
49416
|
if (!options.dryRun) {
|
|
48138
49417
|
printAgentGitIdentityTipIfNeeded();
|
|
48139
49418
|
}
|
|
@@ -49756,7 +51035,7 @@ function promptbookifyAiText(text) {
|
|
|
49756
51035
|
/**
|
|
49757
51036
|
* Constant for default agent kit model name.
|
|
49758
51037
|
*/
|
|
49759
|
-
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-
|
|
51038
|
+
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.4-mini';
|
|
49760
51039
|
/**
|
|
49761
51040
|
* Creates one structured log entry for streamed tool-call updates.
|
|
49762
51041
|
*
|
|
@@ -50806,6 +52085,7 @@ class AgentLlmExecutionTools {
|
|
|
50806
52085
|
* @param agentSource The agent source string that defines the agent's behavior
|
|
50807
52086
|
*/
|
|
50808
52087
|
constructor(options) {
|
|
52088
|
+
var _a;
|
|
50809
52089
|
this.options = options;
|
|
50810
52090
|
/**
|
|
50811
52091
|
* Cached model requirements to avoid re-parsing the agent source
|
|
@@ -50815,6 +52095,7 @@ class AgentLlmExecutionTools {
|
|
|
50815
52095
|
* Cached parsed agent information
|
|
50816
52096
|
*/
|
|
50817
52097
|
this._cachedAgentInfo = null;
|
|
52098
|
+
this.precomputedModelRequirements = (_a = options.precomputedModelRequirements) !== null && _a !== void 0 ? _a : null;
|
|
50818
52099
|
}
|
|
50819
52100
|
/**
|
|
50820
52101
|
* Updates the agent source and clears the cache
|
|
@@ -50822,9 +52103,13 @@ class AgentLlmExecutionTools {
|
|
|
50822
52103
|
* @param agentSource The new agent source string
|
|
50823
52104
|
*/
|
|
50824
52105
|
updateAgentSource(agentSource) {
|
|
52106
|
+
if (this.options.agentSource === agentSource) {
|
|
52107
|
+
return;
|
|
52108
|
+
}
|
|
50825
52109
|
this.options.agentSource = agentSource;
|
|
50826
52110
|
this._cachedAgentInfo = null;
|
|
50827
52111
|
this._cachedModelRequirements = null;
|
|
52112
|
+
this.precomputedModelRequirements = null;
|
|
50828
52113
|
}
|
|
50829
52114
|
/**
|
|
50830
52115
|
* Get cached or parse agent information
|
|
@@ -50841,6 +52126,16 @@ class AgentLlmExecutionTools {
|
|
|
50841
52126
|
* Note: [🐤] This is names `getModelRequirements` *(not `getAgentModelRequirements`)* because in future these two will be united
|
|
50842
52127
|
*/
|
|
50843
52128
|
async getModelRequirements() {
|
|
52129
|
+
var _a, _b;
|
|
52130
|
+
if (this.precomputedModelRequirements !== null) {
|
|
52131
|
+
if (this.options.isVerbose) {
|
|
52132
|
+
console.info('[🤰]', 'Using precomputed agent model requirements', {
|
|
52133
|
+
agent: this.title,
|
|
52134
|
+
toolCount: (_b = (_a = this.precomputedModelRequirements.tools) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
|
|
52135
|
+
});
|
|
52136
|
+
}
|
|
52137
|
+
return this.precomputedModelRequirements;
|
|
52138
|
+
}
|
|
50844
52139
|
if (this._cachedModelRequirements === null) {
|
|
50845
52140
|
const preparationStartedAtMs = Date.now();
|
|
50846
52141
|
if (this.options.isVerbose) {
|
|
@@ -50950,6 +52245,7 @@ class AgentLlmExecutionTools {
|
|
|
50950
52245
|
* Resolves agent requirements, attachments, and runtime overrides into one forwarded chat prompt.
|
|
50951
52246
|
*/
|
|
50952
52247
|
async prepareChatPrompt(prompt) {
|
|
52248
|
+
var _a;
|
|
50953
52249
|
const chatPrompt = this.requireChatPrompt(prompt);
|
|
50954
52250
|
const { sanitizedRequirements, promptSuffix } = await this.getSanitizedAgentModelRequirements();
|
|
50955
52251
|
const attachments = normalizeChatAttachments(chatPrompt.attachments);
|
|
@@ -50967,7 +52263,16 @@ class AgentLlmExecutionTools {
|
|
|
50967
52263
|
mergedTools,
|
|
50968
52264
|
knowledgeSourcesForAgent,
|
|
50969
52265
|
});
|
|
50970
|
-
|
|
52266
|
+
if (this.options.isVerbose) {
|
|
52267
|
+
console.info('[🤰]', 'Prepared agent chat prompt', {
|
|
52268
|
+
agent: this.title,
|
|
52269
|
+
usedPrecomputedModelRequirements: this.precomputedModelRequirements !== null,
|
|
52270
|
+
toolNames: mergedTools.map((tool) => tool.name),
|
|
52271
|
+
knowledgeSourcesCount: (_a = knowledgeSourcesForAgent === null || knowledgeSourcesForAgent === void 0 ? void 0 : knowledgeSourcesForAgent.length) !== null && _a !== void 0 ? _a : 0,
|
|
52272
|
+
promptSuffixLength: promptSuffix.length,
|
|
52273
|
+
systemMessageLength: sanitizedRequirements.systemMessage.length,
|
|
52274
|
+
});
|
|
52275
|
+
}
|
|
50971
52276
|
return {
|
|
50972
52277
|
forwardedPrompt,
|
|
50973
52278
|
sanitizedRequirements,
|
|
@@ -51154,6 +52459,7 @@ class AgentLlmExecutionTools {
|
|
|
51154
52459
|
* Runs one prepared prompt through the deprecated OpenAI Assistant backend.
|
|
51155
52460
|
*/
|
|
51156
52461
|
async callOpenAiAssistantChatModelStream(options) {
|
|
52462
|
+
var _a, _b, _c, _d;
|
|
51157
52463
|
const assistant = await this.getOrPrepareOpenAiAssistant({
|
|
51158
52464
|
llmTools: options.llmTools,
|
|
51159
52465
|
originalPrompt: options.originalPrompt,
|
|
@@ -51161,7 +52467,14 @@ class AgentLlmExecutionTools {
|
|
|
51161
52467
|
onProgress: options.onProgress,
|
|
51162
52468
|
});
|
|
51163
52469
|
const promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools = createOpenAiAssistantPrompt(options.preparedChatPrompt.forwardedPrompt);
|
|
51164
|
-
|
|
52470
|
+
if (this.options.isVerbose) {
|
|
52471
|
+
console.info('[🤰]', 'Prepared OpenAI Assistant prompt', {
|
|
52472
|
+
agent: this.title,
|
|
52473
|
+
toolNames: (_b = (_a = promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools.modelRequirements.tools) === null || _a === void 0 ? void 0 : _a.map((tool) => tool.name)) !== null && _b !== void 0 ? _b : [],
|
|
52474
|
+
knowledgeSourcesCount: (_d = (_c = promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools.modelRequirements
|
|
52475
|
+
.knowledgeSources) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0,
|
|
52476
|
+
});
|
|
52477
|
+
}
|
|
51165
52478
|
return assistant.callChatModelStream(promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools, options.onProgress, options.streamOptions);
|
|
51166
52479
|
}
|
|
51167
52480
|
/**
|
|
@@ -51852,7 +53165,8 @@ class Agent extends AgentLlmExecutionTools {
|
|
|
51852
53165
|
isVerbose: options.isVerbose,
|
|
51853
53166
|
llmTools: getSingleLlmExecutionTools(options.executionTools.llm),
|
|
51854
53167
|
assistantPreparationMode: options.assistantPreparationMode,
|
|
51855
|
-
agentSource: agentSource.value,
|
|
53168
|
+
agentSource: agentSource.value,
|
|
53169
|
+
precomputedModelRequirements: options.precomputedModelRequirements,
|
|
51856
53170
|
});
|
|
51857
53171
|
this._agentName = undefined;
|
|
51858
53172
|
/**
|