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