@promptbook/cli 0.114.0-11 → 0.114.0-13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +8 -1
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +193 -63
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +25 -28
- package/apps/agents-server/src/app/admin/messages/send-email/emailTesting.ts +153 -0
- package/apps/agents-server/src/app/admin/messages/send-email/emailTestingAccess.ts +30 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +18 -5
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +7 -4
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentGoalChatPlannedMessages.tsx +23 -8
- package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +14 -3
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +2 -20
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +2 -1
- package/apps/agents-server/src/app/agents/[agentName]/projectReferenceActions.ts +61 -0
- package/apps/agents-server/src/app/api/internal/agent-goal-chat-planned-messages/route.ts +34 -7
- package/apps/agents-server/src/components/AgentProjects/resolveLatestCompletedAgentMessageKey.ts +32 -0
- package/apps/agents-server/src/components/AgentProjects/useAgentProjectMarkdownReferences.ts +124 -2
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +3 -0
- package/apps/agents-server/src/languages/translations/czech.yaml +3 -0
- package/apps/agents-server/src/languages/translations/english.yaml +3 -0
- package/apps/agents-server/src/message-providers/email/stalwart/StalwartMessageProvider.ts +2 -1
- package/apps/agents-server/src/tools/agentGoalChatTimeoutToolFunctions.ts +24 -2
- package/apps/agents-server/src/tools/agentGoalChatTimeoutTools.ts +5 -4
- package/apps/agents-server/src/utils/agentGoalChat/agentGoalChatPlannedMessageActions.ts +38 -9
- package/apps/agents-server/src/utils/agentGoalChat/createAgentGoalChatNoteContent.ts +15 -6
- package/apps/agents-server/src/utils/agentProjects/listAgentProjectNames.ts +35 -0
- package/apps/agents-server/src/utils/agentProjects/listAgentProjects.ts +6 -23
- package/apps/agents-server/src/utils/agentProjects/resolveAgentChatProjectReferences.ts +25 -0
- package/apps/agents-server/src/utils/chatMessageChips/createAnsweredMessageChipToolCalls.ts +37 -0
- package/apps/agents-server/src/utils/chatMessageChips/createPlannedMessageChipToolCalls.ts +39 -0
- package/apps/agents-server/src/utils/chatMessageChips/createTouchedProjectChipToolCalls.ts +75 -0
- package/apps/agents-server/src/utils/email/agentEmailAddress.ts +14 -0
- package/apps/agents-server/src/utils/email/sendEmailThroughStalwart.ts +2 -1
- package/apps/agents-server/src/utils/localChatRunner/applyLocalAgentPlannedMessageCommands.ts +212 -0
- package/apps/agents-server/src/utils/localChatRunner/prepareLocalAgentPlannedMessagesSidecar.ts +66 -0
- package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +110 -22
- package/apps/agents-server/src/utils/userChat/persistUserChatJobTerminalState.ts +5 -0
- package/apps/agents-server/src/utils/userChat/userChatJobRunReport.ts +30 -0
- package/apps/agents-server/src/utils/userChatTimeout/createTimeoutWakeUpMessage.ts +9 -1
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore/repeatFiredUserChatTimeout.ts +74 -0
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutStore.ts +1 -0
- package/apps/agents-server/src/utils/userChatTimeout/userChatTimeoutWorker.ts +63 -54
- package/esm/index.es.js +1042 -194
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +22 -4
- package/esm/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +3 -3
- package/esm/scripts/run-agent-messages/messages/loadAgentPlannedMessagesSidecar.d.ts +10 -0
- package/esm/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +15 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
- package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
- package/esm/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
- package/esm/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
- package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
- package/esm/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
- package/esm/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
- package/esm/src/book-3.0/AgentMessageRunReport.d.ts +28 -0
- package/esm/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +94 -0
- package/esm/src/book-3.0/AgentPlannedMessagesSidecar.test.d.ts +1 -0
- package/esm/src/book-3.0/createAgentMessageSidecarBaseName.d.ts +13 -0
- package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
- package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
- package/esm/src/book-components/Chat/utils/agentProjectToolCall.d.ts +46 -0
- package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +24 -0
- package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
- package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
- package/esm/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
- package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
- package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
- package/esm/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
- package/esm/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
- package/esm/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +59 -0
- package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedProjectNames.d.ts +19 -0
- package/esm/src/utils/agent-message-runtime/resolveAgentMessageTouchedProjectNames.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/book-2.0/agent-source/AgentSourceParseResult.ts +6 -0
- package/src/book-2.0/agent-source/ParsedUnknownCommitment.ts +29 -0
- package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +38 -1
- package/src/book-2.0/agent-source/getBookCommitmentLineType.ts +36 -0
- package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.ts +128 -61
- package/src/book-3.0/AgentMessageRunReport.ts +76 -0
- package/src/book-3.0/AgentPlannedMessagesSidecar.ts +229 -0
- package/src/book-3.0/AgentTeamConversationWorkspace.ts +3 -5
- package/src/book-3.0/createAgentMessageSidecarBaseName.ts +20 -0
- package/src/book-components/BookEditor/BookEditorMonaco.tsx +6 -1
- package/src/book-components/BookEditor/BookEditorMonacoTokenization.ts +3 -1
- package/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.ts +50 -0
- package/src/book-components/Chat/Chat/Chat.module.css +6 -1
- package/src/book-components/Chat/Chat/renderToolCallDetails.tsx +1 -12
- package/src/book-components/Chat/utils/agentProjectToolCall.ts +74 -0
- package/src/book-components/Chat/utils/getToolCallChipletInfo.ts +28 -0
- package/src/book-components/Chat/utils/timeoutToolCallPresentation.ts +23 -3
- package/src/cli/cli-commands/coder/boilerplateTemplates.ts +3 -15
- package/src/cli/cli-commands/coder/init.ts +11 -2
- package/src/cli/cli-commands/coder/ping.ts +11 -1
- package/src/cli/cli-commands/coder/run.ts +11 -2
- package/src/cli/cli-commands/coder/server.ts +11 -2
- package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +5 -0
- package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +17 -5
- package/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts +6 -3
- package/src/cli/cli-commands/common/harness/HarnessInstallationStatus.ts +3 -2
- package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +49 -0
- package/src/executables/platforms/locateAppOnWindows.ts +12 -9
- package/src/other/templates/getTemplatesPipelineCollection.ts +740 -860
- package/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.ts +105 -0
- package/src/utils/agent-message-runtime/resolveAgentMessageRuntimeActivity.ts +6 -54
- package/src/utils/agent-message-runtime/resolveAgentMessageTouchedProjectNames.ts +147 -0
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -1
- package/umd/index.umd.js +1041 -193
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-agent-messages/messages/buildAgentGoalChatPromptSection.d.ts +22 -4
- package/umd/scripts/run-agent-messages/messages/buildAgentMessagePrompt.d.ts +3 -3
- package/umd/scripts/run-agent-messages/messages/loadAgentPlannedMessagesSidecar.d.ts +10 -0
- package/umd/scripts/run-agent-messages/messages/resolveTouchedAgentProjects.d.ts +15 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
- package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
- package/umd/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
- package/umd/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
- package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
- package/umd/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
- package/umd/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
- package/umd/src/book-3.0/AgentMessageRunReport.d.ts +28 -0
- package/umd/src/book-3.0/AgentPlannedMessagesSidecar.d.ts +94 -0
- package/umd/src/book-3.0/AgentPlannedMessagesSidecar.test.d.ts +1 -0
- package/umd/src/book-3.0/createAgentMessageSidecarBaseName.d.ts +13 -0
- package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
- package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
- package/umd/src/book-components/Chat/utils/agentProjectToolCall.d.ts +46 -0
- package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +24 -0
- package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
- package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
- package/umd/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
- package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
- package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
- package/umd/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
- package/umd/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
- package/umd/src/utils/agent-message-runtime/parseAgentMessageRuntimeLogEvents.d.ts +59 -0
- package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedProjectNames.d.ts +19 -0
- package/umd/src/utils/agent-message-runtime/resolveAgentMessageTouchedProjectNames.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
- package/esm/apps/agents-server/src/utils/agentGoalChat/agentGoalChatConstants.d.ts +0 -12
- package/umd/apps/agents-server/src/utils/agentGoalChat/agentGoalChatConstants.d.ts +0 -12
package/esm/index.es.js
CHANGED
|
@@ -4,7 +4,7 @@ import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
|
4
4
|
import { writeFile, stat, mkdir, readFile, readdir, rm, cp, lstat, symlink, rename, unlink, appendFile, realpath, copyFile, access, constants, watch, rmdir } from 'fs/promises';
|
|
5
5
|
import { join, delimiter, relative, basename, resolve, dirname, isAbsolute, extname } from 'path';
|
|
6
6
|
import { createHash, randomBytes } from 'crypto';
|
|
7
|
-
import { spawn } from 'child_process';
|
|
7
|
+
import { spawn, spawnSync } from 'child_process';
|
|
8
8
|
import moment from 'moment';
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import { createWriteStream, mkdirSync, writeFileSync, statSync, readFileSync, existsSync, promises } from 'fs';
|
|
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
48
48
|
* @generated
|
|
49
49
|
* @see https://github.com/webgptorg/promptbook
|
|
50
50
|
*/
|
|
51
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.114.0-13';
|
|
52
52
|
/**
|
|
53
53
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
54
54
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -4088,10 +4088,6 @@ const PTBK_AGENTS_SERVER_URL_ENV = 'PTBK_AGENTS_SERVER_URL';
|
|
|
4088
4088
|
* Environment variable containing the internal worker token for local agent runners.
|
|
4089
4089
|
*/
|
|
4090
4090
|
const PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN_ENV = 'PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN';
|
|
4091
|
-
/**
|
|
4092
|
-
* Header used by internal local-runner routes.
|
|
4093
|
-
*/
|
|
4094
|
-
const USER_CHAT_WORKER_TOKEN_HEADER = 'x-user-chat-worker-token';
|
|
4095
4091
|
|
|
4096
4092
|
/**
|
|
4097
4093
|
* Format either small or big number
|
|
@@ -20307,6 +20303,30 @@ const COMMITMENT_REGISTRY = [
|
|
|
20307
20303
|
// TODO: [🧠] Maybe create through standardized $register
|
|
20308
20304
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
20309
20305
|
|
|
20306
|
+
/**
|
|
20307
|
+
* Pattern shared by Book parsing and editor highlighting for a commitment-like line.
|
|
20308
|
+
*
|
|
20309
|
+
* A Book commitment keyword consists of one or more all-uppercase words with at least
|
|
20310
|
+
* two characters each. This deliberately mirrors the Book editor syntax rule so an
|
|
20311
|
+
* unknown keyword starts a new source block everywhere it is interpreted.
|
|
20312
|
+
*
|
|
20313
|
+
* @private internal constant of `getBookCommitmentLineType`
|
|
20314
|
+
*/
|
|
20315
|
+
const BOOK_COMMITMENT_LINE_TYPE_REGEX = /^\s*(?<type>[A-Z][A-Z0-9]+(?:\s+[A-Z][A-Z0-9]+)*)(?=\s|$)/;
|
|
20316
|
+
/**
|
|
20317
|
+
* Extracts the uppercase commitment type from a Book line, including types not registered by Promptbook.
|
|
20318
|
+
*
|
|
20319
|
+
* @param line - One raw Book source line.
|
|
20320
|
+
* @returns Matched commitment type or `null` when the line does not begin with commitment syntax.
|
|
20321
|
+
*
|
|
20322
|
+
* @private internal utility of Book parsing and `<BookEditor/>`
|
|
20323
|
+
*/
|
|
20324
|
+
function getBookCommitmentLineType(line) {
|
|
20325
|
+
var _a;
|
|
20326
|
+
const match = BOOK_COMMITMENT_LINE_TYPE_REGEX.exec(line);
|
|
20327
|
+
return ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.type) || null;
|
|
20328
|
+
}
|
|
20329
|
+
|
|
20310
20330
|
/**
|
|
20311
20331
|
* Consumes the agent-name prelude from raw source.
|
|
20312
20332
|
*
|
|
@@ -20364,13 +20384,15 @@ function parseAgentSourceWithCommitments(agentSource) {
|
|
|
20364
20384
|
return {
|
|
20365
20385
|
agentName: null,
|
|
20366
20386
|
commitments: [],
|
|
20387
|
+
unknownCommitments: [],
|
|
20367
20388
|
nonCommitmentLines: [],
|
|
20368
20389
|
};
|
|
20369
20390
|
}
|
|
20370
20391
|
const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
|
|
20371
20392
|
const commitments = [];
|
|
20393
|
+
const unknownCommitments = [];
|
|
20372
20394
|
const nonCommitmentLines = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]] : [];
|
|
20373
|
-
// Parse commitments with multiline support
|
|
20395
|
+
// Parse commitments with multiline support.
|
|
20374
20396
|
let currentCommitment = null;
|
|
20375
20397
|
// Process lines starting from after the agent name line
|
|
20376
20398
|
const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
|
|
@@ -20384,25 +20406,11 @@ function parseAgentSourceWithCommitments(agentSource) {
|
|
|
20384
20406
|
// Check if this line starts or ends a code block
|
|
20385
20407
|
if (trimmedLine.startsWith('```')) {
|
|
20386
20408
|
isInsideCodeBlock = !isInsideCodeBlock;
|
|
20387
|
-
|
|
20388
|
-
// If we are inside a commitment, the code block is part of it
|
|
20389
|
-
currentCommitment.contentLines.push(line);
|
|
20390
|
-
}
|
|
20391
|
-
else {
|
|
20392
|
-
// If we are not inside a commitment, the code block is non-commitment
|
|
20393
|
-
nonCommitmentLines.push(line);
|
|
20394
|
-
}
|
|
20409
|
+
appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
|
|
20395
20410
|
continue;
|
|
20396
20411
|
}
|
|
20397
20412
|
if (isInsideCodeBlock) {
|
|
20398
|
-
|
|
20399
|
-
// If we are inside a commitment and a code block, the line is part of the commitment
|
|
20400
|
-
currentCommitment.contentLines.push(line);
|
|
20401
|
-
}
|
|
20402
|
-
else {
|
|
20403
|
-
// If we are inside a code block but not a commitment, the line is non-commitment
|
|
20404
|
-
nonCommitmentLines.push(line);
|
|
20405
|
-
}
|
|
20413
|
+
appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
|
|
20406
20414
|
continue;
|
|
20407
20415
|
}
|
|
20408
20416
|
// Check if this line starts a new commitment
|
|
@@ -20411,22 +20419,14 @@ function parseAgentSourceWithCommitments(agentSource) {
|
|
|
20411
20419
|
const typeRegex = definition.createTypeRegex();
|
|
20412
20420
|
const match = typeRegex.exec(line.trim());
|
|
20413
20421
|
if (match && ((_a = match.groups) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
20414
|
-
|
|
20415
|
-
if (currentCommitment) {
|
|
20416
|
-
const fullContent = currentCommitment.contentLines.join('\n');
|
|
20417
|
-
commitments.push({
|
|
20418
|
-
type: currentCommitment.type,
|
|
20419
|
-
content: spaceTrim$1(fullContent),
|
|
20420
|
-
originalLine: currentCommitment.originalStartLine,
|
|
20421
|
-
lineNumber: currentCommitment.startLineNumber,
|
|
20422
|
-
});
|
|
20423
|
-
}
|
|
20422
|
+
appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
|
|
20424
20423
|
// Extract the initial content from the commitment line
|
|
20425
20424
|
const fullRegex = definition.createRegex();
|
|
20426
20425
|
const fullMatch = fullRegex.exec(line.trim());
|
|
20427
20426
|
const initialContent = ((_b = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _b === void 0 ? void 0 : _b.contents) || '';
|
|
20428
20427
|
// Start a new commitment
|
|
20429
20428
|
currentCommitment = {
|
|
20429
|
+
kind: 'known',
|
|
20430
20430
|
type: definition.type,
|
|
20431
20431
|
startLineNumber: i + 1,
|
|
20432
20432
|
originalStartLine: line,
|
|
@@ -20436,52 +20436,90 @@ function parseAgentSourceWithCommitments(agentSource) {
|
|
|
20436
20436
|
break;
|
|
20437
20437
|
}
|
|
20438
20438
|
}
|
|
20439
|
+
if (foundNewCommitment) {
|
|
20440
|
+
continue;
|
|
20441
|
+
}
|
|
20442
|
+
const unknownCommitmentType = getBookCommitmentLineType(line);
|
|
20443
|
+
if (unknownCommitmentType) {
|
|
20444
|
+
appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
|
|
20445
|
+
currentCommitment = {
|
|
20446
|
+
kind: 'unknown',
|
|
20447
|
+
type: unknownCommitmentType,
|
|
20448
|
+
startLineNumber: i + 1,
|
|
20449
|
+
originalStartLine: line,
|
|
20450
|
+
sourceLines: [line],
|
|
20451
|
+
};
|
|
20452
|
+
continue;
|
|
20453
|
+
}
|
|
20439
20454
|
// Check if this is a horizontal line (ends any current commitment)
|
|
20440
20455
|
const isHorizontalLine = HORIZONTAL_LINE_PATTERN$1.test(line);
|
|
20441
20456
|
if (isHorizontalLine) {
|
|
20442
|
-
|
|
20443
|
-
|
|
20444
|
-
const fullContent = currentCommitment.contentLines.join('\n');
|
|
20445
|
-
commitments.push({
|
|
20446
|
-
type: currentCommitment.type,
|
|
20447
|
-
content: spaceTrim$1(fullContent),
|
|
20448
|
-
originalLine: currentCommitment.originalStartLine,
|
|
20449
|
-
lineNumber: currentCommitment.startLineNumber,
|
|
20450
|
-
});
|
|
20451
|
-
currentCommitment = null;
|
|
20452
|
-
}
|
|
20457
|
+
appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
|
|
20458
|
+
currentCommitment = null;
|
|
20453
20459
|
// Add horizontal line to non-commitment lines
|
|
20454
20460
|
nonCommitmentLines.push(line);
|
|
20455
20461
|
continue;
|
|
20456
20462
|
}
|
|
20457
|
-
|
|
20458
|
-
if (currentCommitment) {
|
|
20459
|
-
// This line belongs to the current commitment
|
|
20460
|
-
currentCommitment.contentLines.push(line);
|
|
20461
|
-
}
|
|
20462
|
-
else {
|
|
20463
|
-
// This line is not part of any commitment
|
|
20464
|
-
nonCommitmentLines.push(line);
|
|
20465
|
-
}
|
|
20466
|
-
}
|
|
20467
|
-
}
|
|
20468
|
-
// Don't forget to save the last commitment if it exists
|
|
20469
|
-
if (currentCommitment) {
|
|
20470
|
-
const fullContent = currentCommitment.contentLines.join('\n');
|
|
20471
|
-
commitments.push({
|
|
20472
|
-
type: currentCommitment.type,
|
|
20473
|
-
content: spaceTrim$1(fullContent),
|
|
20474
|
-
originalLine: currentCommitment.originalStartLine,
|
|
20475
|
-
lineNumber: currentCommitment.startLineNumber,
|
|
20476
|
-
});
|
|
20463
|
+
appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
|
|
20477
20464
|
}
|
|
20465
|
+
appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
|
|
20478
20466
|
return {
|
|
20479
20467
|
agentName,
|
|
20480
20468
|
agentNameLineNumber,
|
|
20481
20469
|
commitments,
|
|
20470
|
+
unknownCommitments,
|
|
20482
20471
|
nonCommitmentLines,
|
|
20483
20472
|
};
|
|
20484
20473
|
}
|
|
20474
|
+
/**
|
|
20475
|
+
* Stores the current known or unknown commitment in its appropriate parsed collection.
|
|
20476
|
+
*
|
|
20477
|
+
* @param currentCommitment - Commitment-like block collected so far.
|
|
20478
|
+
* @param commitments - Parsed registered commitments to append to.
|
|
20479
|
+
* @param unknownCommitments - Parsed unregistered commitments to append to.
|
|
20480
|
+
*
|
|
20481
|
+
* @private internal utility of `parseAgentSourceWithCommitments`
|
|
20482
|
+
*/
|
|
20483
|
+
function appendCurrentCommitment(currentCommitment, commitments, unknownCommitments) {
|
|
20484
|
+
if (!currentCommitment) {
|
|
20485
|
+
return;
|
|
20486
|
+
}
|
|
20487
|
+
if (currentCommitment.kind === 'unknown') {
|
|
20488
|
+
unknownCommitments.push({
|
|
20489
|
+
type: currentCommitment.type,
|
|
20490
|
+
originalLine: currentCommitment.originalStartLine,
|
|
20491
|
+
lineNumber: currentCommitment.startLineNumber,
|
|
20492
|
+
source: spaceTrim$1(currentCommitment.sourceLines.join('\n')),
|
|
20493
|
+
});
|
|
20494
|
+
return;
|
|
20495
|
+
}
|
|
20496
|
+
commitments.push({
|
|
20497
|
+
type: currentCommitment.type,
|
|
20498
|
+
content: spaceTrim$1(currentCommitment.contentLines.join('\n')),
|
|
20499
|
+
originalLine: currentCommitment.originalStartLine,
|
|
20500
|
+
lineNumber: currentCommitment.startLineNumber,
|
|
20501
|
+
});
|
|
20502
|
+
}
|
|
20503
|
+
/**
|
|
20504
|
+
* Adds one source line to the active commitment-like block, or to plain source content when none is active.
|
|
20505
|
+
*
|
|
20506
|
+
* @param currentCommitment - Commitment-like block currently being collected.
|
|
20507
|
+
* @param line - Source line to append.
|
|
20508
|
+
* @param nonCommitmentLines - Plain source lines to append to when no block is active.
|
|
20509
|
+
*
|
|
20510
|
+
* @private internal utility of `parseAgentSourceWithCommitments`
|
|
20511
|
+
*/
|
|
20512
|
+
function appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines) {
|
|
20513
|
+
if (!currentCommitment) {
|
|
20514
|
+
nonCommitmentLines.push(line);
|
|
20515
|
+
return;
|
|
20516
|
+
}
|
|
20517
|
+
if (currentCommitment.kind === 'unknown') {
|
|
20518
|
+
currentCommitment.sourceLines.push(line);
|
|
20519
|
+
return;
|
|
20520
|
+
}
|
|
20521
|
+
currentCommitment.contentLines.push(line);
|
|
20522
|
+
}
|
|
20485
20523
|
|
|
20486
20524
|
/**
|
|
20487
20525
|
* Parses parameters from text using both supported notations:
|
|
@@ -21840,7 +21878,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21840
21878
|
return agentBookStats.isFile();
|
|
21841
21879
|
}
|
|
21842
21880
|
catch (error) {
|
|
21843
|
-
if (isFileNotFoundError$
|
|
21881
|
+
if (isFileNotFoundError$7(error)) {
|
|
21844
21882
|
return false;
|
|
21845
21883
|
}
|
|
21846
21884
|
throw error;
|
|
@@ -21849,7 +21887,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21849
21887
|
/**
|
|
21850
21888
|
* Returns true when one filesystem error indicates a missing path.
|
|
21851
21889
|
*/
|
|
21852
|
-
function isFileNotFoundError$
|
|
21890
|
+
function isFileNotFoundError$7(error) {
|
|
21853
21891
|
return Boolean(error &&
|
|
21854
21892
|
typeof error === 'object' &&
|
|
21855
21893
|
'code' in error &&
|
|
@@ -21879,7 +21917,7 @@ async function readQueuedMessageDirectoryEntries(queuedMessagesDirectoryPath) {
|
|
|
21879
21917
|
return await readdir(queuedMessagesDirectoryPath, { withFileTypes: true });
|
|
21880
21918
|
}
|
|
21881
21919
|
catch (error) {
|
|
21882
|
-
if (isFileNotFoundError$
|
|
21920
|
+
if (isFileNotFoundError$6(error)) {
|
|
21883
21921
|
return [];
|
|
21884
21922
|
}
|
|
21885
21923
|
throw error;
|
|
@@ -21900,7 +21938,7 @@ function normalizeRelativePath$2(relativePath) {
|
|
|
21900
21938
|
/**
|
|
21901
21939
|
* Returns true when an error is a missing-path filesystem error.
|
|
21902
21940
|
*/
|
|
21903
|
-
function isFileNotFoundError$
|
|
21941
|
+
function isFileNotFoundError$6(error) {
|
|
21904
21942
|
return Boolean(error &&
|
|
21905
21943
|
typeof error === 'object' &&
|
|
21906
21944
|
'code' in error &&
|
|
@@ -22378,7 +22416,7 @@ async function runGitCommand(options) {
|
|
|
22378
22416
|
lastIndexLockState = await readGitIndexLockState(cwd, options.env);
|
|
22379
22417
|
if ((lastIndexLockState === null || lastIndexLockState === void 0 ? void 0 : lastIndexLockState.isStale) && !isStaleIndexLockRemoved) {
|
|
22380
22418
|
await unlink(lastIndexLockState.path).catch((unlinkError) => {
|
|
22381
|
-
if (isFileNotFoundError$
|
|
22419
|
+
if (isFileNotFoundError$5(unlinkError)) {
|
|
22382
22420
|
return;
|
|
22383
22421
|
}
|
|
22384
22422
|
throw unlinkError;
|
|
@@ -22439,7 +22477,7 @@ async function readGitIndexLockState(cwd, env) {
|
|
|
22439
22477
|
};
|
|
22440
22478
|
}
|
|
22441
22479
|
catch (error) {
|
|
22442
|
-
if (isFileNotFoundError$
|
|
22480
|
+
if (isFileNotFoundError$5(error)) {
|
|
22443
22481
|
return undefined;
|
|
22444
22482
|
}
|
|
22445
22483
|
throw error;
|
|
@@ -22496,7 +22534,7 @@ function formatDelay$1(delayMs) {
|
|
|
22496
22534
|
/**
|
|
22497
22535
|
* Detects missing-file errors while inspecting the lock file.
|
|
22498
22536
|
*/
|
|
22499
|
-
function isFileNotFoundError$
|
|
22537
|
+
function isFileNotFoundError$5(error) {
|
|
22500
22538
|
return Boolean(error &&
|
|
22501
22539
|
typeof error === 'object' &&
|
|
22502
22540
|
'code' in error &&
|
|
@@ -24751,14 +24789,90 @@ const PTBK_CODER_LOG_FILE_ENV_NAME = 'PTBK_CODER_LOG_FILE';
|
|
|
24751
24789
|
* without the generated script and the prompt it embeds.
|
|
24752
24790
|
*/
|
|
24753
24791
|
const SCRIPT_EXECUTION_LOG_RAW_OUTPUT_MARKER = '--- raw output ---';
|
|
24792
|
+
/**
|
|
24793
|
+
* Command which terminates the Bash process tree rooted at the running harness.
|
|
24794
|
+
*
|
|
24795
|
+
* Bash's POSIX process IDs differ from Windows process IDs in Git Bash, so the Windows branch resolves the native PID
|
|
24796
|
+
* before delegating to `taskkill`. Unix harnesses run in a dedicated Bash job process group, which can be terminated
|
|
24797
|
+
* with its negative process ID.
|
|
24798
|
+
*/
|
|
24799
|
+
const TERMINATE_BASH_PROCESS_TREE_COMMAND = process.platform === 'win32'
|
|
24800
|
+
? spaceTrim$1(`
|
|
24801
|
+
HARNESS_WINDOWS_PROCESS_ID="$(ps -l -p "$HARNESS_PROCESS_ID" | awk 'NR == 2 { print $4 }')"
|
|
24802
|
+
if [ -n "$HARNESS_WINDOWS_PROCESS_ID" ]; then
|
|
24803
|
+
MSYS_NO_PATHCONV=1 taskkill.exe /PID "$HARNESS_WINDOWS_PROCESS_ID" /T /F > /dev/null 2>&1 || true
|
|
24804
|
+
fi
|
|
24805
|
+
`)
|
|
24806
|
+
: 'kill -TERM -- "-$HARNESS_PROCESS_ID" 2>/dev/null || true';
|
|
24807
|
+
/**
|
|
24808
|
+
* Shell condition that detects whether the Node process which owns the harness is still running.
|
|
24809
|
+
*
|
|
24810
|
+
* Git Bash translates process IDs and command switches, so querying the native Windows PID needs both `tasklist` and
|
|
24811
|
+
* disabled MSYS path conversion there. Unix can use the native `kill -0` process existence check.
|
|
24812
|
+
*/
|
|
24813
|
+
const IS_PARENT_PROCESS_RUNNING_CONDITION = process.platform === 'win32'
|
|
24814
|
+
? 'MSYS_NO_PATHCONV=1 tasklist.exe /FI "PID eq $PARENT_CODER_PROCESS_ID" /NH | awk -v processId="$PARENT_CODER_PROCESS_ID" \'$2 == processId { isFound = 1 } END { exit isFound ? 0 : 1 }\''
|
|
24815
|
+
: 'kill -0 "$PARENT_CODER_PROCESS_ID" 2>/dev/null';
|
|
24816
|
+
/**
|
|
24817
|
+
* Environment variable that identifies the Node process responsible for a temporary harness shell.
|
|
24818
|
+
*/
|
|
24819
|
+
const PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME = 'PTBK_CODER_PARENT_PROCESS_ID';
|
|
24754
24820
|
/**
|
|
24755
24821
|
* Small bash wrapper that preserves stdout/stderr streams while teeing both into the runtime log file.
|
|
24822
|
+
*
|
|
24823
|
+
* A watcher polls the owning Node process by its PID. If that process exits abruptly, the watcher stops the whole
|
|
24824
|
+
* Bash and harness process tree instead of letting it continue as an orphan.
|
|
24756
24825
|
*/
|
|
24757
24826
|
const LOGGED_BASH_WRAPPER_COMMAND = spaceTrim$1(`
|
|
24827
|
+
PARENT_CODER_PROCESS_ID="\${${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}}"
|
|
24828
|
+
unset ${PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME}
|
|
24829
|
+
|
|
24830
|
+
terminate_harness_process_tree() {
|
|
24831
|
+
# Keep the EXIT cleanup intact so the wrapper can also stop its parent-process watcher.
|
|
24832
|
+
trap - HUP INT TERM
|
|
24833
|
+
${TERMINATE_BASH_PROCESS_TREE_COMMAND}
|
|
24834
|
+
}
|
|
24835
|
+
|
|
24836
|
+
is_parent_process_running() {
|
|
24837
|
+
${IS_PARENT_PROCESS_RUNNING_CONDITION}
|
|
24838
|
+
}
|
|
24839
|
+
|
|
24758
24840
|
if [ -n "\${${PTBK_CODER_LOG_FILE_ENV_NAME}:-}" ]; then
|
|
24759
|
-
exec > >(tee -a "
|
|
24841
|
+
exec > >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}") 2> >(tee -a "$${PTBK_CODER_LOG_FILE_ENV_NAME}" >&2)
|
|
24842
|
+
fi
|
|
24843
|
+
|
|
24844
|
+
watch_parent_process() {
|
|
24845
|
+
trap 'exit 0' HUP INT TERM
|
|
24846
|
+
|
|
24847
|
+
while is_parent_process_running; do
|
|
24848
|
+
sleep 1
|
|
24849
|
+
done
|
|
24850
|
+
|
|
24851
|
+
terminate_harness_process_tree
|
|
24852
|
+
}
|
|
24853
|
+
|
|
24854
|
+
# Background harness jobs need their own process group on Unix so termination cannot reach the parent coder.
|
|
24855
|
+
set -m
|
|
24856
|
+
if ! is_parent_process_running; then
|
|
24857
|
+
exit 1
|
|
24760
24858
|
fi
|
|
24761
|
-
bash "$1"
|
|
24859
|
+
bash "$1" &
|
|
24860
|
+
HARNESS_PROCESS_ID=$!
|
|
24861
|
+
|
|
24862
|
+
cleanup_parent_process_watcher() {
|
|
24863
|
+
kill "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
|
|
24864
|
+
wait "$PARENT_PROCESS_WATCHER_PID" 2>/dev/null || true
|
|
24865
|
+
}
|
|
24866
|
+
|
|
24867
|
+
watch_parent_process &
|
|
24868
|
+
PARENT_PROCESS_WATCHER_PID=$!
|
|
24869
|
+
|
|
24870
|
+
trap cleanup_parent_process_watcher EXIT
|
|
24871
|
+
trap terminate_harness_process_tree HUP INT TERM
|
|
24872
|
+
|
|
24873
|
+
wait "$HARNESS_PROCESS_ID"
|
|
24874
|
+
SCRIPT_EXIT_CODE=$?
|
|
24875
|
+
exit "$SCRIPT_EXIT_CODE"
|
|
24762
24876
|
`);
|
|
24763
24877
|
/**
|
|
24764
24878
|
* Shapes one bash invocation that optionally mirrors live script output into a temporary log file.
|
|
@@ -24814,6 +24928,38 @@ function describeTempScriptKind(scriptPath) {
|
|
|
24814
24928
|
return scriptPath.toLowerCase().endsWith('.test.sh') ? 'test shell' : 'runner shell';
|
|
24815
24929
|
}
|
|
24816
24930
|
|
|
24931
|
+
/**
|
|
24932
|
+
* Standard streams used by every temporary Bash runner.
|
|
24933
|
+
*/
|
|
24934
|
+
const BASH_PROCESS_STDIO = ['pipe', 'pipe', 'pipe'];
|
|
24935
|
+
/**
|
|
24936
|
+
* Whether the current Node process is running on Windows.
|
|
24937
|
+
*/
|
|
24938
|
+
const IS_WINDOWS$1 = process.platform === 'win32';
|
|
24939
|
+
/**
|
|
24940
|
+
* Starts one temporary Bash script in a process tree owned by the current Node process.
|
|
24941
|
+
*
|
|
24942
|
+
* The wrapper watches the supplied owning process ID. When that process exits abruptly, the wrapper terminates every
|
|
24943
|
+
* nested shell and harness process instead of leaving it orphaned.
|
|
24944
|
+
*
|
|
24945
|
+
* @private internal utility of the coding prompt runner
|
|
24946
|
+
*/
|
|
24947
|
+
function $spawnLoggedBashScript(options) {
|
|
24948
|
+
var _a;
|
|
24949
|
+
const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
|
|
24950
|
+
const parentProcessId = (_a = options.parentProcessId) !== null && _a !== void 0 ? _a : process.pid;
|
|
24951
|
+
return spawn('bash', bashExecution.args, {
|
|
24952
|
+
detached: !IS_WINDOWS$1,
|
|
24953
|
+
env: {
|
|
24954
|
+
...process.env,
|
|
24955
|
+
...bashExecution.env,
|
|
24956
|
+
[PTBK_CODER_PARENT_PROCESS_ID_ENV_NAME]: parentProcessId.toString(),
|
|
24957
|
+
},
|
|
24958
|
+
stdio: BASH_PROCESS_STDIO,
|
|
24959
|
+
windowsHide: true,
|
|
24960
|
+
});
|
|
24961
|
+
}
|
|
24962
|
+
|
|
24817
24963
|
/**
|
|
24818
24964
|
* Prints one live shell output chunk to the terminal when console mirroring is enabled.
|
|
24819
24965
|
*/
|
|
@@ -24836,12 +24982,12 @@ function printLiveScriptChunk(chunk, source, shouldPrintLiveOutput) {
|
|
|
24836
24982
|
async function runBashScriptWithOutput(options) {
|
|
24837
24983
|
var _a;
|
|
24838
24984
|
await appendScriptExecutionLogStart(options);
|
|
24839
|
-
const bashExecution = buildLoggedBashExecution(options.scriptPath, options.logPath);
|
|
24840
24985
|
const scriptPathPosix = toPosixPath(options.scriptPath);
|
|
24841
24986
|
const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
|
|
24842
24987
|
return await new Promise((resolve, reject) => {
|
|
24843
|
-
const commandProcess =
|
|
24844
|
-
|
|
24988
|
+
const commandProcess = $spawnLoggedBashScript({
|
|
24989
|
+
scriptPath: options.scriptPath,
|
|
24990
|
+
logPath: options.logPath,
|
|
24845
24991
|
});
|
|
24846
24992
|
let output = '';
|
|
24847
24993
|
let settled = false;
|
|
@@ -24900,8 +25046,9 @@ async function runBashScriptWithOutput(options) {
|
|
|
24900
25046
|
const failure = new Error(spaceTrim$1(output) || `Command "bash ${scriptPathPosix}" exited with code ${code}`);
|
|
24901
25047
|
settleWithLog(`failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`, () => reject(failure), failure);
|
|
24902
25048
|
};
|
|
25049
|
+
// Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
|
|
25050
|
+
// substitutions after the direct shell exits.
|
|
24903
25051
|
commandProcess.on('close', handleExit);
|
|
24904
|
-
commandProcess.on('exit', handleExit);
|
|
24905
25052
|
commandProcess.on('disconnect', () => {
|
|
24906
25053
|
const failure = new Error(`Command "bash ${scriptPathPosix}" disconnected`);
|
|
24907
25054
|
settleWithLog('failed after disconnect', () => reject(failure), failure);
|
|
@@ -25762,6 +25909,35 @@ function isGitHubCopilotArgumentLimitError(error) {
|
|
|
25762
25909
|
error.message.includes('bin/node'));
|
|
25763
25910
|
}
|
|
25764
25911
|
|
|
25912
|
+
/**
|
|
25913
|
+
* Whether the current coder process runs on Windows.
|
|
25914
|
+
*/
|
|
25915
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
25916
|
+
/**
|
|
25917
|
+
* Stops one active temporary Bash shell together with the harness process tree it owns.
|
|
25918
|
+
*
|
|
25919
|
+
* Unix sends `SIGTERM` to the wrapper so its shell trap terminates the separately grouped harness job. Windows does
|
|
25920
|
+
* not reliably deliver that signal to Bash, so the native `taskkill` command terminates the direct shell tree instead.
|
|
25921
|
+
*
|
|
25922
|
+
* @private internal utility of the coding prompt runner
|
|
25923
|
+
*/
|
|
25924
|
+
function $terminateLoggedBashProcessTree(commandProcess) {
|
|
25925
|
+
if (commandProcess.exitCode !== null || commandProcess.signalCode !== null) {
|
|
25926
|
+
return;
|
|
25927
|
+
}
|
|
25928
|
+
if (!IS_WINDOWS) {
|
|
25929
|
+
commandProcess.kill('SIGTERM');
|
|
25930
|
+
return;
|
|
25931
|
+
}
|
|
25932
|
+
if (!commandProcess.pid) {
|
|
25933
|
+
return;
|
|
25934
|
+
}
|
|
25935
|
+
spawnSync('taskkill.exe', ['/PID', commandProcess.pid.toString(), '/T', '/F'], {
|
|
25936
|
+
stdio: 'ignore',
|
|
25937
|
+
windowsHide: true,
|
|
25938
|
+
});
|
|
25939
|
+
}
|
|
25940
|
+
|
|
25765
25941
|
/**
|
|
25766
25942
|
* Maximum number of output characters included in thrown error messages.
|
|
25767
25943
|
*/
|
|
@@ -25804,15 +25980,16 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
25804
25980
|
const scriptPathPosix = toPosixPath(scriptPath);
|
|
25805
25981
|
const shouldPrintLiveOutput = (_a = options.shouldPrintLiveOutput) !== null && _a !== void 0 ? _a : true;
|
|
25806
25982
|
await appendScriptExecutionLogStart(options);
|
|
25807
|
-
const bashExecution = buildLoggedBashExecution(scriptPath, options.logPath);
|
|
25808
25983
|
return await new Promise((resolve, reject) => {
|
|
25809
|
-
const commandProcess =
|
|
25810
|
-
|
|
25984
|
+
const commandProcess = $spawnLoggedBashScript({
|
|
25985
|
+
scriptPath,
|
|
25986
|
+
logPath: options.logPath,
|
|
25811
25987
|
});
|
|
25812
25988
|
let stdoutBuffer = '';
|
|
25813
25989
|
let stderrBuffer = '';
|
|
25814
25990
|
let fullOutput = '';
|
|
25815
25991
|
let markerSeen = false;
|
|
25992
|
+
let isCompletedAfterIdleTimeout = false;
|
|
25816
25993
|
let idleTimer;
|
|
25817
25994
|
let settled = false;
|
|
25818
25995
|
let isSettling = false;
|
|
@@ -25854,15 +26031,16 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
25854
26031
|
});
|
|
25855
26032
|
};
|
|
25856
26033
|
/**
|
|
25857
|
-
*
|
|
26034
|
+
* Stops a marker-completed process tree after its trailing output becomes idle.
|
|
25858
26035
|
*/
|
|
25859
26036
|
const finishAfterIdleTimeout = () => {
|
|
25860
|
-
|
|
26037
|
+
isCompletedAfterIdleTimeout = true;
|
|
26038
|
+
idleTimer = undefined;
|
|
25861
26039
|
try {
|
|
25862
|
-
commandProcess
|
|
26040
|
+
$terminateLoggedBashProcessTree(commandProcess);
|
|
25863
26041
|
}
|
|
25864
26042
|
catch (_a) {
|
|
25865
|
-
//
|
|
26043
|
+
// The close handler below still settles normally when Bash already exited before the idle timer fired.
|
|
25866
26044
|
}
|
|
25867
26045
|
};
|
|
25868
26046
|
/**
|
|
@@ -25921,7 +26099,11 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
25921
26099
|
const failure = code === 0 || markerSeen
|
|
25922
26100
|
? undefined
|
|
25923
26101
|
: new Error(buildCommandFailureMessage(scriptPathPosix, code, fullOutput));
|
|
25924
|
-
const status =
|
|
26102
|
+
const status = isCompletedAfterIdleTimeout
|
|
26103
|
+
? 'completed after idle timeout'
|
|
26104
|
+
: failure
|
|
26105
|
+
? `failed with exit code ${code !== null && code !== void 0 ? code : 'unknown'}`
|
|
26106
|
+
: 'succeeded';
|
|
25925
26107
|
settleWithLog(status, () => {
|
|
25926
26108
|
if (!failure) {
|
|
25927
26109
|
resolve(fullOutput);
|
|
@@ -25930,8 +26112,9 @@ async function runScriptUntilMarkerIdle(options) {
|
|
|
25930
26112
|
reject(failure);
|
|
25931
26113
|
});
|
|
25932
26114
|
};
|
|
26115
|
+
// Wait for `close`, not only `exit`, because the Bash wrapper can still be flushing its tee process
|
|
26116
|
+
// substitutions after the direct shell exits.
|
|
25933
26117
|
commandProcess.on('close', handleExit);
|
|
25934
|
-
commandProcess.on('exit', handleExit);
|
|
25935
26118
|
commandProcess.on('disconnect', () => {
|
|
25936
26119
|
const failure = new Error(buildCommandFailureMessage(scriptPathPosix, null, fullOutput));
|
|
25937
26120
|
settleWithLog('failed after disconnect', () => reject(failure), failure);
|
|
@@ -29427,7 +29610,7 @@ async function readWorkingTreeFileHash(projectPath, relativePath) {
|
|
|
29427
29610
|
return createHash('sha1').update(content).digest('hex');
|
|
29428
29611
|
}
|
|
29429
29612
|
catch (error) {
|
|
29430
|
-
if (isFileNotFoundError$
|
|
29613
|
+
if (isFileNotFoundError$4(error)) {
|
|
29431
29614
|
return null;
|
|
29432
29615
|
}
|
|
29433
29616
|
throw error;
|
|
@@ -29442,7 +29625,7 @@ function normalizeGitFilePath(filePath) {
|
|
|
29442
29625
|
/**
|
|
29443
29626
|
* Returns true when an error is a missing-file filesystem error.
|
|
29444
29627
|
*/
|
|
29445
|
-
function isFileNotFoundError$
|
|
29628
|
+
function isFileNotFoundError$4(error) {
|
|
29446
29629
|
return Boolean(error &&
|
|
29447
29630
|
typeof error === 'object' &&
|
|
29448
29631
|
'code' in error &&
|
|
@@ -30435,7 +30618,7 @@ function buildAgentProjectsPromptSection(options = {}) {
|
|
|
30435
30618
|
- You have 100% control over every project folder: create, read, modify, and delete files there, run scripts and commands inside it, and keep any persistent data you need between conversations.
|
|
30436
30619
|
- When the user asks you to build something (for example a website) or to do longer-term work, create a new project folder \`${AGENT_PROJECTS_DIRECTORY_PATH}/<project-name>/\` (use a short kebab-case name) and do the work inside it. Modify an existing project when the user refers to work you already did.
|
|
30437
30620
|
- A project can be a git repository — you can run \`git init\` inside a project folder and commit your work there.
|
|
30438
|
-
- Do not touch anything outside the \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory
|
|
30621
|
+
- Do not touch anything outside the \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory beyond the file changes allowed at the top of this prompt.
|
|
30439
30622
|
|
|
30440
30623
|
${block(buildProjectReferenceInstructions(projectsUrlPath))}
|
|
30441
30624
|
${block(buildProjectRuntimeInstructions(options.projectRuntimeApi))}
|
|
@@ -30491,55 +30674,166 @@ function buildProjectRuntimeInstructions(projectRuntimeApi) {
|
|
|
30491
30674
|
}
|
|
30492
30675
|
|
|
30493
30676
|
/**
|
|
30494
|
-
*
|
|
30677
|
+
* Milliseconds in one second.
|
|
30678
|
+
*
|
|
30679
|
+
* @private internal timeout-chat constant
|
|
30680
|
+
*/
|
|
30681
|
+
const SECOND_IN_MILLISECONDS = 1000;
|
|
30682
|
+
/**
|
|
30683
|
+
* Seconds in one minute.
|
|
30684
|
+
*
|
|
30685
|
+
* @private internal timeout-chat constant
|
|
30686
|
+
*/
|
|
30687
|
+
const MINUTE_IN_SECONDS = 60;
|
|
30688
|
+
/**
|
|
30689
|
+
* Seconds in one hour.
|
|
30690
|
+
*
|
|
30691
|
+
* @private internal timeout-chat constant
|
|
30692
|
+
*/
|
|
30693
|
+
const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS;
|
|
30694
|
+
/**
|
|
30695
|
+
* Seconds in one day.
|
|
30696
|
+
*
|
|
30697
|
+
* @private internal timeout-chat constant
|
|
30698
|
+
*/
|
|
30699
|
+
const HOURS_IN_DAY = 24;
|
|
30700
|
+
/**
|
|
30701
|
+
* Seconds in one day.
|
|
30702
|
+
*
|
|
30703
|
+
* @private internal timeout-chat constant
|
|
30495
30704
|
*/
|
|
30705
|
+
const DAY_IN_SECONDS = HOURS_IN_DAY * HOUR_IN_SECONDS;
|
|
30496
30706
|
/**
|
|
30497
|
-
*
|
|
30707
|
+
* Converts timeout duration into natural language sentence fragments.
|
|
30708
|
+
*
|
|
30709
|
+
* Shared by every timeout surface: the chat chips, the goal-chat planned messages of the Agents
|
|
30710
|
+
* Server, and the planned-message prompt section of the agent-folder runner.
|
|
30711
|
+
*
|
|
30712
|
+
* @param milliseconds - Duration or repeat interval in milliseconds.
|
|
30713
|
+
* @returns Label such as `5 minutes` or `1 hour 30 minutes`.
|
|
30714
|
+
*
|
|
30715
|
+
* @private internal utility of `<Chat/>` and the Agents Server planned messages
|
|
30498
30716
|
*/
|
|
30499
|
-
|
|
30717
|
+
function formatTimeoutDurationHuman(milliseconds) {
|
|
30718
|
+
const normalizedMilliseconds = Math.max(0, Math.floor(milliseconds));
|
|
30719
|
+
const totalSeconds = Math.max(1, Math.floor(normalizedMilliseconds / SECOND_IN_MILLISECONDS));
|
|
30720
|
+
if (totalSeconds < MINUTE_IN_SECONDS) {
|
|
30721
|
+
return formatTimeoutUnit(totalSeconds, 'second');
|
|
30722
|
+
}
|
|
30723
|
+
if (totalSeconds < HOUR_IN_SECONDS) {
|
|
30724
|
+
const roundedMinutes = Math.max(1, Math.round(totalSeconds / MINUTE_IN_SECONDS));
|
|
30725
|
+
return formatTimeoutUnit(roundedMinutes, 'minute');
|
|
30726
|
+
}
|
|
30727
|
+
if (totalSeconds < DAY_IN_SECONDS) {
|
|
30728
|
+
const hours = Math.floor(totalSeconds / HOUR_IN_SECONDS);
|
|
30729
|
+
const minutes = Math.floor((totalSeconds % HOUR_IN_SECONDS) / MINUTE_IN_SECONDS);
|
|
30730
|
+
if (minutes === 0) {
|
|
30731
|
+
return formatTimeoutUnit(hours, 'hour');
|
|
30732
|
+
}
|
|
30733
|
+
return `${formatTimeoutUnit(hours, 'hour')} ${formatTimeoutUnit(minutes, 'minute')}`;
|
|
30734
|
+
}
|
|
30735
|
+
const days = Math.floor(totalSeconds / DAY_IN_SECONDS);
|
|
30736
|
+
const hours = Math.floor((totalSeconds % DAY_IN_SECONDS) / HOUR_IN_SECONDS);
|
|
30737
|
+
if (hours === 0) {
|
|
30738
|
+
return formatTimeoutUnit(days, 'day');
|
|
30739
|
+
}
|
|
30740
|
+
return `${formatTimeoutUnit(days, 'day')} ${formatTimeoutUnit(hours, 'hour')}`;
|
|
30741
|
+
}
|
|
30742
|
+
/**
|
|
30743
|
+
* Formats one pluralized timeout unit.
|
|
30744
|
+
*
|
|
30745
|
+
* @private internal timeout-chat helper
|
|
30746
|
+
*/
|
|
30747
|
+
function formatTimeoutUnit(value, unit) {
|
|
30748
|
+
return `${value} ${unit}${value === 1 ? '' : 's'}`;
|
|
30749
|
+
}
|
|
30750
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
30500
30751
|
|
|
30501
30752
|
/**
|
|
30502
30753
|
* Builds the planned-message instructions available to an Agents Server-managed coding agent.
|
|
30503
30754
|
*
|
|
30504
|
-
* Planned messages always target the agent's singleton goal chat, regardless of the chat in which
|
|
30505
|
-
*
|
|
30755
|
+
* Planned messages always target the agent's singleton goal chat, regardless of the chat in which the
|
|
30756
|
+
* wake-up is planned, and they repeat like `setInterval` until they are cancelled. The sidecar file is
|
|
30757
|
+
* the only channel that changes them, so an answer that merely claims a follow-up was planned changes
|
|
30758
|
+
* nothing — and an answer that changes nothing keeps the current plan running.
|
|
30759
|
+
*
|
|
30760
|
+
* @param sidecar - Sidecar prepared by the Agents Server for the answered message.
|
|
30761
|
+
* @returns Prompt section, or an empty string outside Agents Server-managed runs.
|
|
30506
30762
|
*/
|
|
30507
|
-
function buildAgentGoalChatPromptSection(
|
|
30508
|
-
if (!
|
|
30763
|
+
function buildAgentGoalChatPromptSection(sidecar) {
|
|
30764
|
+
if (!sidecar) {
|
|
30509
30765
|
return '';
|
|
30510
30766
|
}
|
|
30511
|
-
return spaceTrim$1(`
|
|
30512
|
-
|
|
30767
|
+
return spaceTrim$1((block) => `
|
|
30768
|
+
## Planned goal-chat messages
|
|
30513
30769
|
|
|
30514
|
-
|
|
30770
|
+
You can plan a message that repeatedly wakes you in your singleton goal chat. A planned message works like \`setInterval\`, not like \`setTimeout\`: once planned, it keeps waking you at its interval until you cancel it. This works during every Agents Server-managed chat invocation, not only while answering inside the goal chat.
|
|
30515
30771
|
|
|
30516
|
-
|
|
30517
|
-
- Use \`set_timeout\` when useful work should resume later. The delay is a positive number of milliseconds and the message should say exactly what your future invocation needs to do.
|
|
30518
|
-
- Use \`cancel_timeout\` with the returned \`timeoutId\` when a planned message is no longer useful.
|
|
30519
|
-
- A fired message appears in the goal chat and invokes you there. If more future work remains, plan the next useful message during that invocation.
|
|
30772
|
+
${block(buildCurrentPlannedMessagesLines(sidecar.currentPlannedMessages))}
|
|
30520
30773
|
|
|
30521
|
-
|
|
30774
|
+
Editing \`${sidecar.relativeSidecarPath}\` is the **only** way to change what wakes you. Writing in your answer that you scheduled something does not schedule anything.
|
|
30522
30775
|
|
|
30523
|
-
|
|
30524
|
-
|
|
30525
|
-
|
|
30526
|
-
|
|
30527
|
-
|
|
30528
|
-
|
|
30776
|
+
- **Keeping your planned messages as they are is the default.** When the list above already matches your goal, leave \`commands\` empty — every listed message keeps repeating on its own, and re-planning it would only duplicate it.
|
|
30777
|
+
- When your goal needs a repeating wake-up that is not listed above, append \`{"action":"set","milliseconds":<repeat interval>,"message":"<what each future invocation must do>"}\` to the \`commands\` array of that file.
|
|
30778
|
+
- When a listed message no longer matches your goal (for example your goal now says every 10 minutes while the message repeats every 5 minutes), append \`{"action":"cancel","timeoutId":"<timeout id>"}\` and one \`set\` command with the interval your goal really requires.
|
|
30779
|
+
- When your goal no longer needs a listed message at all, cancel it without planning a replacement.
|
|
30780
|
+
- Never edit \`version\`, \`agentPermanentId\`, or \`currentPlannedMessages\`.
|
|
30781
|
+
- The Agents Server applies every command once your answer is finished, and each repetition appears in the goal chat and invokes you there.
|
|
30782
|
+
- Only mention a planned follow-up in your answer when it is really planned: either you appended the matching \`set\` command, or you kept one of the messages listed above.
|
|
30783
|
+
`);
|
|
30784
|
+
}
|
|
30785
|
+
/**
|
|
30786
|
+
* Renders the already planned messages so the agent can compare them with its goal.
|
|
30787
|
+
*
|
|
30788
|
+
* @param currentPlannedMessages - Planned messages prepared by the Agents Server.
|
|
30789
|
+
* @returns Markdown list, or a sentence stating that nothing is planned yet.
|
|
30790
|
+
*
|
|
30791
|
+
* @private function of `buildAgentGoalChatPromptSection`
|
|
30792
|
+
*/
|
|
30793
|
+
function buildCurrentPlannedMessagesLines(currentPlannedMessages) {
|
|
30794
|
+
if (currentPlannedMessages.length === 0) {
|
|
30795
|
+
return 'You currently have no planned messages waiting.';
|
|
30796
|
+
}
|
|
30797
|
+
const plannedMessageLines = currentPlannedMessages.map(createCurrentPlannedMessageLine);
|
|
30798
|
+
return spaceTrim$1((block) => `
|
|
30799
|
+
These planned messages are already waiting for you, so keep them unless they stopped matching your goal:
|
|
30529
30800
|
|
|
30530
|
-
|
|
30531
|
-
|
|
30532
|
-
|
|
30533
|
-
|
|
30534
|
-
|
|
30801
|
+
${block(plannedMessageLines.join('\n'))}
|
|
30802
|
+
`);
|
|
30803
|
+
}
|
|
30804
|
+
/**
|
|
30805
|
+
* Renders one already planned message with the schedule the agent has to compare with its goal.
|
|
30806
|
+
*
|
|
30807
|
+
* @param plannedMessage - One planned message prepared by the Agents Server.
|
|
30808
|
+
* @returns Markdown list item.
|
|
30809
|
+
*
|
|
30810
|
+
* @private function of `buildAgentGoalChatPromptSection`
|
|
30811
|
+
*/
|
|
30812
|
+
function createCurrentPlannedMessageLine(plannedMessage) {
|
|
30813
|
+
var _a;
|
|
30814
|
+
const message = ((_a = plannedMessage.message) === null || _a === void 0 ? void 0 : _a.trim()) || 'Continue working towards the current goal.';
|
|
30815
|
+
const schedule = plannedMessage.intervalMs
|
|
30816
|
+
? `repeats every ${formatTimeoutDurationHuman(plannedMessage.intervalMs)}`
|
|
30817
|
+
: `wakes you once at ${plannedMessage.dueAt}`;
|
|
30818
|
+
return `- \`${plannedMessage.timeoutId}\` ${schedule}: ${message}`;
|
|
30819
|
+
}
|
|
30535
30820
|
|
|
30536
|
-
|
|
30537
|
-
|
|
30538
|
-
|
|
30539
|
-
|
|
30540
|
-
|
|
30541
|
-
|
|
30542
|
-
|
|
30821
|
+
/**
|
|
30822
|
+
* Creates the stable file-system safe base name that belongs to one queued `.book` message.
|
|
30823
|
+
*
|
|
30824
|
+
* Every sidecar the Agents Server prepares next to a queued message (TEAM workspaces, planned-message
|
|
30825
|
+
* files, ...) derives its own name from this one normalization, so a sidecar can always be found again
|
|
30826
|
+
* from the message file name alone.
|
|
30827
|
+
*
|
|
30828
|
+
* @param messageFileName - File name of the queued message, with or without directories.
|
|
30829
|
+
* @returns Normalized base name usable as a directory or file name.
|
|
30830
|
+
*
|
|
30831
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30832
|
+
*/
|
|
30833
|
+
function createAgentMessageSidecarBaseName(messageFileName) {
|
|
30834
|
+
const rawBaseName = basename(messageFileName).replace(/\.book$/iu, '');
|
|
30835
|
+
const normalizedBaseName = rawBaseName.replace(/[^A-Za-z0-9._-]+/gu, '-').replace(/^[._-]+|[._-]+$/gu, '');
|
|
30836
|
+
return normalizedBaseName || 'message';
|
|
30543
30837
|
}
|
|
30544
30838
|
|
|
30545
30839
|
// Note: [💞] This file defines the shared TEAM workspace convention rather than one standalone entity.
|
|
@@ -30573,9 +30867,7 @@ const AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME = 'teammates';
|
|
|
30573
30867
|
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30574
30868
|
*/
|
|
30575
30869
|
function createAgentTeamConversationWorkspaceDirectoryName(messageFileName) {
|
|
30576
|
-
|
|
30577
|
-
const normalizedBaseName = rawBaseName.replace(/[^A-Za-z0-9._-]+/gu, '-').replace(/^[._-]+|[._-]+$/gu, '');
|
|
30578
|
-
return normalizedBaseName || 'message';
|
|
30870
|
+
return createAgentMessageSidecarBaseName(messageFileName);
|
|
30579
30871
|
}
|
|
30580
30872
|
/**
|
|
30581
30873
|
* Creates the relative active workspace path for one queued message.
|
|
@@ -30645,8 +30937,8 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30645
30937
|
return '';
|
|
30646
30938
|
}
|
|
30647
30939
|
const teammateSections = workspace.manifest.teammates.map((teammate) => {
|
|
30648
|
-
const sourcePath = toPromptPath(join(workspace.relativeWorkspacePath, AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME, teammate.sourceFileName));
|
|
30649
|
-
const transcriptPath = toPromptPath(join(workspace.relativeWorkspacePath, `${teammate.permanentId}--01.book`));
|
|
30940
|
+
const sourcePath = toPromptPath$1(join(workspace.relativeWorkspacePath, AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME, teammate.sourceFileName));
|
|
30941
|
+
const transcriptPath = toPromptPath$1(join(workspace.relativeWorkspacePath, `${teammate.permanentId}--01.book`));
|
|
30650
30942
|
const instructions = teammate.instructions.trim() || 'No additional TEAM instructions were provided.';
|
|
30651
30943
|
return spaceTrim$1(`
|
|
30652
30944
|
- ${teammate.agentName}
|
|
@@ -30664,7 +30956,7 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30664
30956
|
|
|
30665
30957
|
- The underlying coding harness is already executing this turn **exactly once**. Never start, queue, or invoke another coding harness, agent runner, or \`ptbk\` command to consult a teammate.
|
|
30666
30958
|
- The primary agent remains responsible for the final user-facing \`MESSAGE @Agent\` answer.
|
|
30667
|
-
- Teammate source files are read-only context. When a teammate consultation occurs, record the actual exchange in a new top-level \`.book\` file in \`${toPromptPath(workspace.relativeWorkspacePath)}\`.
|
|
30959
|
+
- Teammate source files are read-only context. When a teammate consultation occurs, record the actual exchange in a new top-level \`.book\` file in \`${toPromptPath$1(workspace.relativeWorkspacePath)}\`.
|
|
30668
30960
|
- Name each transcript \`<teammate permanent id>--<sequence>.book\`, for example \`${workspace.manifest.teammates[0].permanentId}--01.book\`.
|
|
30669
30961
|
- Every transcript must contain alternating \`MESSAGE @${workspace.manifest.primaryAgent.agentName}\` and \`MESSAGE @<teammate name>\` blocks. Do not create a transcript for a teammate that was not consulted.
|
|
30670
30962
|
`);
|
|
@@ -30672,7 +30964,7 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30672
30964
|
/**
|
|
30673
30965
|
* Converts a filesystem-relative path into the portable path notation used in prompts.
|
|
30674
30966
|
*/
|
|
30675
|
-
function toPromptPath(path) {
|
|
30967
|
+
function toPromptPath$1(path) {
|
|
30676
30968
|
return path.replace(/\\/gu, '/');
|
|
30677
30969
|
}
|
|
30678
30970
|
|
|
@@ -30685,7 +30977,7 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30685
30977
|
|
|
30686
30978
|
- Read \`${messageRelativePath}\` and answer the most recent \`MESSAGE\` block. In a normal chat it is from \`@User\`; in your internal goal chat it can be from \`@Agent\`.
|
|
30687
30979
|
- Only change the queued message file by appending one new \`MESSAGE @Agent\` block
|
|
30688
|
-
${block(buildAllowedFileChangesPromptLine(options
|
|
30980
|
+
${block(buildAllowedFileChangesPromptLine(options))}
|
|
30689
30981
|
|
|
30690
30982
|
## Rules for the answering
|
|
30691
30983
|
|
|
@@ -30721,7 +31013,7 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30721
31013
|
|
|
30722
31014
|
- You can use \`message\` for message that will be sent immediately after clicking the button, or \`messageDraft\` for message that will be prefilled in the input field for editing before sending.
|
|
30723
31015
|
|
|
30724
|
-
${block(buildAgentGoalChatPromptSection(options.
|
|
31016
|
+
${block(buildAgentGoalChatPromptSection(options.plannedMessagesSidecar))}
|
|
30725
31017
|
|
|
30726
31018
|
${block(buildAgentProjectsPromptSection(options))}
|
|
30727
31019
|
|
|
@@ -30735,11 +31027,30 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30735
31027
|
/**
|
|
30736
31028
|
* Explains the exact files that one coding harness may change for a user turn.
|
|
30737
31029
|
*/
|
|
30738
|
-
function buildAllowedFileChangesPromptLine(
|
|
30739
|
-
|
|
30740
|
-
|
|
31030
|
+
function buildAllowedFileChangesPromptLine(options) {
|
|
31031
|
+
const allowedFileChanges = [`files inside your own \`${AGENT_PROJECTS_DIRECTORY_PATH}/\` directory`];
|
|
31032
|
+
if (options.teamWorkspace) {
|
|
31033
|
+
allowedFileChanges.push(`new consultation transcripts inside \`${toPromptPath(options.teamWorkspace.relativeWorkspacePath)}\``);
|
|
31034
|
+
}
|
|
31035
|
+
if (options.plannedMessagesSidecar) {
|
|
31036
|
+
allowedFileChanges.push(`the \`commands\` array of \`${toPromptPath(options.plannedMessagesSidecar.relativeSidecarPath)}\``);
|
|
31037
|
+
}
|
|
31038
|
+
return `- Do not modify any other file in the repository, except ${formatAllowedFileChanges(allowedFileChanges)}`;
|
|
31039
|
+
}
|
|
31040
|
+
/**
|
|
31041
|
+
* Joins the allowed file-change phrases into one readable enumeration.
|
|
31042
|
+
*/
|
|
31043
|
+
function formatAllowedFileChanges(allowedFileChanges) {
|
|
31044
|
+
if (allowedFileChanges.length === 1) {
|
|
31045
|
+
return allowedFileChanges[0];
|
|
30741
31046
|
}
|
|
30742
|
-
return
|
|
31047
|
+
return `${allowedFileChanges.slice(0, -1).join(', ')} and ${allowedFileChanges[allowedFileChanges.length - 1]}`;
|
|
31048
|
+
}
|
|
31049
|
+
/**
|
|
31050
|
+
* Converts a filesystem-relative path into the portable path notation used in prompts.
|
|
31051
|
+
*/
|
|
31052
|
+
function toPromptPath(path) {
|
|
31053
|
+
return path.replace(/\\/gu, '/');
|
|
30743
31054
|
}
|
|
30744
31055
|
|
|
30745
31056
|
var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
|
|
@@ -36801,7 +37112,8 @@ async function augmentAgentModelRequirementsFromSource(requirements, parseResult
|
|
|
36801
37112
|
requirements = await importReferencedFiles(requirements);
|
|
36802
37113
|
requirements = appendMcpServers(requirements, agentSource);
|
|
36803
37114
|
requirements = appendNonCommitmentContent(requirements, parseResult);
|
|
36804
|
-
|
|
37115
|
+
requirements = appendExampleInteractions(requirements, parseResult);
|
|
37116
|
+
return appendUnknownCommitmentContent(requirements, parseResult);
|
|
36805
37117
|
}
|
|
36806
37118
|
/**
|
|
36807
37119
|
* Imports text files referenced by IMPORT commitments and appends their transformed content to the system message.
|
|
@@ -36923,6 +37235,36 @@ function appendNonCommitmentContent(requirements, parseResult) {
|
|
|
36923
37235
|
}
|
|
36924
37236
|
return appendSystemMessageSection(requirements, nonCommitmentContent);
|
|
36925
37237
|
}
|
|
37238
|
+
/**
|
|
37239
|
+
* Appends unknown commitment blocks as final extra context without applying commitment side effects.
|
|
37240
|
+
*
|
|
37241
|
+
* @param requirements - Current requirements snapshot.
|
|
37242
|
+
* @param parseResult - Parsed source including unknown commitment blocks.
|
|
37243
|
+
* @returns Requirements with unknown commitment context appended at the end of the system message.
|
|
37244
|
+
*
|
|
37245
|
+
* @private internal utility of `augmentAgentModelRequirementsFromSource`
|
|
37246
|
+
*/
|
|
37247
|
+
function appendUnknownCommitmentContent(requirements, parseResult) {
|
|
37248
|
+
const unknownCommitmentContent = getUnknownCommitmentContent(parseResult);
|
|
37249
|
+
if (!unknownCommitmentContent) {
|
|
37250
|
+
return requirements;
|
|
37251
|
+
}
|
|
37252
|
+
return appendSystemMessageSection(requirements, unknownCommitmentContent);
|
|
37253
|
+
}
|
|
37254
|
+
/**
|
|
37255
|
+
* Joins unknown commitment blocks into their final extra-context representation.
|
|
37256
|
+
*
|
|
37257
|
+
* @param parseResult - Parsed source including unknown commitment blocks.
|
|
37258
|
+
* @returns Unknown commitment source blocks in their original order.
|
|
37259
|
+
*
|
|
37260
|
+
* @private internal utility of `appendUnknownCommitmentContent`
|
|
37261
|
+
*/
|
|
37262
|
+
function getUnknownCommitmentContent(parseResult) {
|
|
37263
|
+
return parseResult.unknownCommitments
|
|
37264
|
+
.map((commitment) => commitment.source)
|
|
37265
|
+
.filter(Boolean)
|
|
37266
|
+
.join('\n\n');
|
|
37267
|
+
}
|
|
36926
37268
|
/**
|
|
36927
37269
|
* Collects plain-text lines that were not parsed as commitments and should still become part of the final system message.
|
|
36928
37270
|
*
|
|
@@ -37462,12 +37804,12 @@ async function finalizeAgentTeamConversationWorkspace(options) {
|
|
|
37462
37804
|
await rm(finishedWorkspacePath, { recursive: true, force: true });
|
|
37463
37805
|
await mkdir(finishedWorkspacePath, { recursive: true });
|
|
37464
37806
|
const relativePaths = [
|
|
37465
|
-
toPortablePath$
|
|
37807
|
+
toPortablePath$2(join(finishedRelativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME)),
|
|
37466
37808
|
];
|
|
37467
37809
|
await rename(activeManifestPath, join(finishedWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME));
|
|
37468
37810
|
for (const transcriptFileName of transcriptFileNames) {
|
|
37469
37811
|
await rename(join(activeWorkspacePath, transcriptFileName), join(finishedWorkspacePath, transcriptFileName));
|
|
37470
|
-
relativePaths.push(toPortablePath$
|
|
37812
|
+
relativePaths.push(toPortablePath$2(join(finishedRelativeWorkspacePath, transcriptFileName)));
|
|
37471
37813
|
}
|
|
37472
37814
|
await rm(activeWorkspacePath, { recursive: true, force: true });
|
|
37473
37815
|
return { relativePaths };
|
|
@@ -37484,7 +37826,7 @@ async function listAgentTeamTranscriptFileNames(activeWorkspacePath) {
|
|
|
37484
37826
|
.sort((firstFileName, secondFileName) => firstFileName.localeCompare(secondFileName));
|
|
37485
37827
|
}
|
|
37486
37828
|
catch (error) {
|
|
37487
|
-
if (isFileNotFoundError$
|
|
37829
|
+
if (isFileNotFoundError$3(error)) {
|
|
37488
37830
|
return [];
|
|
37489
37831
|
}
|
|
37490
37832
|
throw error;
|
|
@@ -37499,7 +37841,7 @@ async function isExistingPath$1(path) {
|
|
|
37499
37841
|
return true;
|
|
37500
37842
|
}
|
|
37501
37843
|
catch (error) {
|
|
37502
|
-
if (isFileNotFoundError$
|
|
37844
|
+
if (isFileNotFoundError$3(error)) {
|
|
37503
37845
|
return false;
|
|
37504
37846
|
}
|
|
37505
37847
|
throw error;
|
|
@@ -37508,11 +37850,204 @@ async function isExistingPath$1(path) {
|
|
|
37508
37850
|
/**
|
|
37509
37851
|
* Converts a filesystem-relative path into a portable Git path.
|
|
37510
37852
|
*/
|
|
37853
|
+
function toPortablePath$2(path) {
|
|
37854
|
+
return path.replace(/\\/gu, '/');
|
|
37855
|
+
}
|
|
37856
|
+
/**
|
|
37857
|
+
* Returns true when one filesystem error indicates a missing path.
|
|
37858
|
+
*/
|
|
37859
|
+
function isFileNotFoundError$3(error) {
|
|
37860
|
+
return Boolean(error &&
|
|
37861
|
+
typeof error === 'object' &&
|
|
37862
|
+
'code' in error &&
|
|
37863
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR'));
|
|
37864
|
+
}
|
|
37865
|
+
|
|
37866
|
+
// Note: [💞] This file defines the shared planned-message sidecar convention rather than one standalone entity.
|
|
37867
|
+
/**
|
|
37868
|
+
* Relative directory holding the planned-message sidecar of each queued message.
|
|
37869
|
+
*
|
|
37870
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
37871
|
+
*/
|
|
37872
|
+
const AGENT_PLANNED_MESSAGES_DIRECTORY_PATH = join(AGENT_MESSAGES_DIRECTORY_PATH, 'planned');
|
|
37873
|
+
/**
|
|
37874
|
+
* Command actions an agent can request from its planned-message sidecar.
|
|
37875
|
+
*
|
|
37876
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
37877
|
+
*/
|
|
37878
|
+
const AGENT_PLANNED_MESSAGE_COMMAND_ACTIONS = ['set', 'cancel'];
|
|
37879
|
+
/**
|
|
37880
|
+
* Creates the relative planned-message sidecar path of one queued message.
|
|
37881
|
+
*
|
|
37882
|
+
* @param messageFileName - File name of the queued `.book` message.
|
|
37883
|
+
* @returns Relative path of the sidecar inside the agent folder.
|
|
37884
|
+
*
|
|
37885
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
37886
|
+
*/
|
|
37887
|
+
function createAgentPlannedMessagesSidecarPath(messageFileName) {
|
|
37888
|
+
return join(AGENT_PLANNED_MESSAGES_DIRECTORY_PATH, `${createAgentMessageSidecarBaseName(messageFileName)}.json`);
|
|
37889
|
+
}
|
|
37890
|
+
/**
|
|
37891
|
+
* Parses one untrusted planned-message sidecar written back by a coding harness.
|
|
37892
|
+
*
|
|
37893
|
+
* A malformed sidecar never fails the already answered chat, so this returns `null` instead of throwing.
|
|
37894
|
+
*
|
|
37895
|
+
* @param sidecarContent - Raw file content read from the agent folder.
|
|
37896
|
+
* @returns Normalized sidecar, or `null` when it cannot be used.
|
|
37897
|
+
*
|
|
37898
|
+
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
37899
|
+
*/
|
|
37900
|
+
function parseAgentPlannedMessagesSidecar(sidecarContent) {
|
|
37901
|
+
let parsedSidecar;
|
|
37902
|
+
try {
|
|
37903
|
+
parsedSidecar = JSON.parse(sidecarContent);
|
|
37904
|
+
}
|
|
37905
|
+
catch (_a) {
|
|
37906
|
+
return null;
|
|
37907
|
+
}
|
|
37908
|
+
if (!parsedSidecar || typeof parsedSidecar !== 'object' || Array.isArray(parsedSidecar)) {
|
|
37909
|
+
return null;
|
|
37910
|
+
}
|
|
37911
|
+
const sidecar = parsedSidecar;
|
|
37912
|
+
if (sidecar.version !== 1 || typeof sidecar.agentPermanentId !== 'string') {
|
|
37913
|
+
return null;
|
|
37914
|
+
}
|
|
37915
|
+
return {
|
|
37916
|
+
version: 1,
|
|
37917
|
+
agentPermanentId: sidecar.agentPermanentId,
|
|
37918
|
+
currentPlannedMessages: normalizeAgentPlannedMessageSnapshots(sidecar.currentPlannedMessages),
|
|
37919
|
+
commands: normalizeAgentPlannedMessageCommands(sidecar.commands),
|
|
37920
|
+
};
|
|
37921
|
+
}
|
|
37922
|
+
/**
|
|
37923
|
+
* Keeps only the commands whose action is understood by the planned-message actions.
|
|
37924
|
+
*
|
|
37925
|
+
* @param rawCommands - Untrusted `commands` value read from one sidecar.
|
|
37926
|
+
* @returns Commands that can be dispatched, in their original order.
|
|
37927
|
+
*
|
|
37928
|
+
* @private internal utility of `parseAgentPlannedMessagesSidecar`
|
|
37929
|
+
*/
|
|
37930
|
+
function normalizeAgentPlannedMessageCommands(rawCommands) {
|
|
37931
|
+
if (!Array.isArray(rawCommands)) {
|
|
37932
|
+
return [];
|
|
37933
|
+
}
|
|
37934
|
+
return rawCommands.filter(isAgentPlannedMessageCommand);
|
|
37935
|
+
}
|
|
37936
|
+
/**
|
|
37937
|
+
* Checks whether one untrusted sidecar entry names a supported planned-message action.
|
|
37938
|
+
*
|
|
37939
|
+
* @param value - Untrusted command entry.
|
|
37940
|
+
* @returns `true` when the entry can be dispatched.
|
|
37941
|
+
*
|
|
37942
|
+
* @private internal utility of `parseAgentPlannedMessagesSidecar`
|
|
37943
|
+
*/
|
|
37944
|
+
function isAgentPlannedMessageCommand(value) {
|
|
37945
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
37946
|
+
return false;
|
|
37947
|
+
}
|
|
37948
|
+
const action = value.action;
|
|
37949
|
+
return (typeof action === 'string' &&
|
|
37950
|
+
AGENT_PLANNED_MESSAGE_COMMAND_ACTIONS.includes(action));
|
|
37951
|
+
}
|
|
37952
|
+
/**
|
|
37953
|
+
* Keeps only the already planned messages that can be shown to the agent.
|
|
37954
|
+
*
|
|
37955
|
+
* @param rawSnapshots - Untrusted `currentPlannedMessages` value read from one sidecar.
|
|
37956
|
+
* @returns Planned-message snapshots with the fields required for display and cancellation.
|
|
37957
|
+
*
|
|
37958
|
+
* @private internal utility of `parseAgentPlannedMessagesSidecar`
|
|
37959
|
+
*/
|
|
37960
|
+
function normalizeAgentPlannedMessageSnapshots(rawSnapshots) {
|
|
37961
|
+
if (!Array.isArray(rawSnapshots)) {
|
|
37962
|
+
return [];
|
|
37963
|
+
}
|
|
37964
|
+
return rawSnapshots.filter(isAgentPlannedMessageSnapshot).map(createNormalizedAgentPlannedMessageSnapshot);
|
|
37965
|
+
}
|
|
37966
|
+
/**
|
|
37967
|
+
* Normalizes the optional fields of one already planned message.
|
|
37968
|
+
*
|
|
37969
|
+
* @param snapshot - Planned-message entry with the required identity fields.
|
|
37970
|
+
* @returns Snapshot with a usable repeat interval and message.
|
|
37971
|
+
*
|
|
37972
|
+
* @private internal utility of `parseAgentPlannedMessagesSidecar`
|
|
37973
|
+
*/
|
|
37974
|
+
function createNormalizedAgentPlannedMessageSnapshot(snapshot) {
|
|
37975
|
+
const intervalMs = Number(snapshot.intervalMs);
|
|
37976
|
+
return {
|
|
37977
|
+
timeoutId: snapshot.timeoutId,
|
|
37978
|
+
dueAt: snapshot.dueAt,
|
|
37979
|
+
message: typeof snapshot.message === 'string' ? snapshot.message : null,
|
|
37980
|
+
intervalMs: Number.isFinite(intervalMs) && intervalMs > 0 ? Math.floor(intervalMs) : null,
|
|
37981
|
+
};
|
|
37982
|
+
}
|
|
37983
|
+
/**
|
|
37984
|
+
* Checks whether one untrusted sidecar entry describes an already planned message.
|
|
37985
|
+
*
|
|
37986
|
+
* @param value - Untrusted planned-message entry.
|
|
37987
|
+
* @returns `true` when the entry can be shown and cancelled.
|
|
37988
|
+
*
|
|
37989
|
+
* @private internal utility of `parseAgentPlannedMessagesSidecar`
|
|
37990
|
+
*/
|
|
37991
|
+
function isAgentPlannedMessageSnapshot(value) {
|
|
37992
|
+
return Boolean(value &&
|
|
37993
|
+
typeof value === 'object' &&
|
|
37994
|
+
typeof value.timeoutId === 'string' &&
|
|
37995
|
+
typeof value.dueAt === 'string');
|
|
37996
|
+
}
|
|
37997
|
+
|
|
37998
|
+
/**
|
|
37999
|
+
* Loads the planned-message sidecar the Agents Server prepared for one queued message.
|
|
38000
|
+
*
|
|
38001
|
+
* @param projectPath - Absolute path of the current agent runner folder.
|
|
38002
|
+
* @param queuedMessage - Queued message currently being answered.
|
|
38003
|
+
* @returns Sidecar details for the prompt, or `null` outside Agents Server-managed runs.
|
|
38004
|
+
*/
|
|
38005
|
+
async function loadAgentPlannedMessagesSidecar(projectPath, queuedMessage) {
|
|
38006
|
+
const relativeSidecarPath = createAgentPlannedMessagesSidecarPath(queuedMessage.fileName);
|
|
38007
|
+
const sidecarContent = await readOptionalTextFile$2(join(projectPath, relativeSidecarPath));
|
|
38008
|
+
if (sidecarContent === null) {
|
|
38009
|
+
return null;
|
|
38010
|
+
}
|
|
38011
|
+
const sidecar = parseAgentPlannedMessagesSidecar(sidecarContent);
|
|
38012
|
+
if (!sidecar) {
|
|
38013
|
+
return null;
|
|
38014
|
+
}
|
|
38015
|
+
return {
|
|
38016
|
+
relativeSidecarPath: toPortablePath$1(relativeSidecarPath),
|
|
38017
|
+
currentPlannedMessages: sidecar.currentPlannedMessages,
|
|
38018
|
+
};
|
|
38019
|
+
}
|
|
38020
|
+
/**
|
|
38021
|
+
* Reads one text file and treats a missing planned-message sidecar as absent.
|
|
38022
|
+
*
|
|
38023
|
+
* @param path - Absolute sidecar path.
|
|
38024
|
+
* @returns File content, or `null` when the sidecar does not exist.
|
|
38025
|
+
*/
|
|
38026
|
+
async function readOptionalTextFile$2(path) {
|
|
38027
|
+
try {
|
|
38028
|
+
return await readFile(path, 'utf-8');
|
|
38029
|
+
}
|
|
38030
|
+
catch (error) {
|
|
38031
|
+
if (isFileNotFoundError$2(error)) {
|
|
38032
|
+
return null;
|
|
38033
|
+
}
|
|
38034
|
+
throw error;
|
|
38035
|
+
}
|
|
38036
|
+
}
|
|
38037
|
+
/**
|
|
38038
|
+
* Converts a filesystem-relative path to the portable form used in prompts.
|
|
38039
|
+
*
|
|
38040
|
+
* @param path - Relative path inside the agent folder.
|
|
38041
|
+
* @returns Path with forward slashes.
|
|
38042
|
+
*/
|
|
37511
38043
|
function toPortablePath$1(path) {
|
|
37512
38044
|
return path.replace(/\\/gu, '/');
|
|
37513
38045
|
}
|
|
37514
38046
|
/**
|
|
37515
38047
|
* Returns true when one filesystem error indicates a missing path.
|
|
38048
|
+
*
|
|
38049
|
+
* @param error - Caught filesystem failure.
|
|
38050
|
+
* @returns `true` for missing-path errors.
|
|
37516
38051
|
*/
|
|
37517
38052
|
function isFileNotFoundError$2(error) {
|
|
37518
38053
|
return Boolean(error &&
|
|
@@ -37527,7 +38062,7 @@ function isFileNotFoundError$2(error) {
|
|
|
37527
38062
|
async function loadAgentTeamConversationWorkspace(projectPath, queuedMessage) {
|
|
37528
38063
|
const relativeWorkspacePath = createAgentTeamConversationWorkspacePath(queuedMessage.fileName);
|
|
37529
38064
|
const manifestPath = join(projectPath, relativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
|
|
37530
|
-
const manifestContent = await readOptionalTextFile(manifestPath);
|
|
38065
|
+
const manifestContent = await readOptionalTextFile$1(manifestPath);
|
|
37531
38066
|
if (manifestContent === null) {
|
|
37532
38067
|
return null;
|
|
37533
38068
|
}
|
|
@@ -37555,7 +38090,7 @@ function parseAgentTeamConversationWorkspaceManifest(manifestContent) {
|
|
|
37555
38090
|
/**
|
|
37556
38091
|
* Reads one text file and treats a missing team workspace as absent.
|
|
37557
38092
|
*/
|
|
37558
|
-
async function readOptionalTextFile(path) {
|
|
38093
|
+
async function readOptionalTextFile$1(path) {
|
|
37559
38094
|
try {
|
|
37560
38095
|
return await readFile(path, 'utf-8');
|
|
37561
38096
|
}
|
|
@@ -37622,6 +38157,233 @@ function resolveAgentProjectsUrlPath(projectPath) {
|
|
|
37622
38157
|
return `/agents/${encodeURIComponent(agentId)}/projects`;
|
|
37623
38158
|
}
|
|
37624
38159
|
|
|
38160
|
+
/**
|
|
38161
|
+
* Parses every structured event out of one coding-harness runtime log.
|
|
38162
|
+
*
|
|
38163
|
+
* The local agent runner streams the answering harness output into a live runtime log file, one
|
|
38164
|
+
* JSON event per line, optionally prefixed by shell noise. Lines without a usable JSON object are
|
|
38165
|
+
* skipped so a partially written or interleaved log never breaks its consumers.
|
|
38166
|
+
*
|
|
38167
|
+
* @param logText - Raw runtime log content.
|
|
38168
|
+
* @returns Structured events in the order they were streamed.
|
|
38169
|
+
* @private internal utility of the agent-message runtime
|
|
38170
|
+
*/
|
|
38171
|
+
function parseAgentMessageRuntimeLogEvents(logText) {
|
|
38172
|
+
if (!logText) {
|
|
38173
|
+
return [];
|
|
38174
|
+
}
|
|
38175
|
+
const events = [];
|
|
38176
|
+
for (const line of logText.split(/\r?\n/u)) {
|
|
38177
|
+
const event = parseAgentMessageRuntimeLogEvent(line);
|
|
38178
|
+
if (event) {
|
|
38179
|
+
events.push(event);
|
|
38180
|
+
}
|
|
38181
|
+
}
|
|
38182
|
+
return events;
|
|
38183
|
+
}
|
|
38184
|
+
/**
|
|
38185
|
+
* Parses one runtime log line into a structured event when it embeds JSON.
|
|
38186
|
+
*
|
|
38187
|
+
* @param line - One raw runtime log line.
|
|
38188
|
+
* @returns Structured event, or `null` when the line carries no JSON object.
|
|
38189
|
+
* @private internal helper of `parseAgentMessageRuntimeLogEvents`
|
|
38190
|
+
*/
|
|
38191
|
+
function parseAgentMessageRuntimeLogEvent(line) {
|
|
38192
|
+
const jsonStartIndex = line.indexOf('{');
|
|
38193
|
+
if (jsonStartIndex === -1) {
|
|
38194
|
+
return null;
|
|
38195
|
+
}
|
|
38196
|
+
try {
|
|
38197
|
+
const parsedEvent = JSON.parse(line.slice(jsonStartIndex).trim());
|
|
38198
|
+
if (typeof parsedEvent !== 'object' || parsedEvent === null || Array.isArray(parsedEvent)) {
|
|
38199
|
+
return null;
|
|
38200
|
+
}
|
|
38201
|
+
return parsedEvent;
|
|
38202
|
+
}
|
|
38203
|
+
catch (_a) {
|
|
38204
|
+
return null;
|
|
38205
|
+
}
|
|
38206
|
+
}
|
|
38207
|
+
|
|
38208
|
+
/**
|
|
38209
|
+
* Matches one project directory name following the agent `projects/` folder in a file path or command.
|
|
38210
|
+
*
|
|
38211
|
+
* Both path separators are accepted because the local runner answers messages on Unix and Windows,
|
|
38212
|
+
* and JSON-encoded payloads escape backslashes.
|
|
38213
|
+
*
|
|
38214
|
+
* @private internal constant of agent-message touched projects
|
|
38215
|
+
*/
|
|
38216
|
+
const TOUCHED_PROJECT_PATH_REGEX = new RegExp(`${AGENT_PROJECTS_DIRECTORY_PATH}[\\\\/]+([^\\\\/"'\\s,;:]+)`, 'giu');
|
|
38217
|
+
/**
|
|
38218
|
+
* Resolves which projects one coding harness touched while answering a single message.
|
|
38219
|
+
*
|
|
38220
|
+
* The runtime log streams what the harness really did, so a project counts as touched when the
|
|
38221
|
+
* harness named it in a tool invocation — reading, searching, running, or editing files inside
|
|
38222
|
+
* `projects/<project-name>/`. Only tool payloads are inspected: tool results and assistant
|
|
38223
|
+
* narration are ignored so a project merely mentioned in the conversation is never reported.
|
|
38224
|
+
*
|
|
38225
|
+
* Every candidate is matched against the projects that really exist, which both filters prompt
|
|
38226
|
+
* placeholders such as `projects/<project-name>/` and keeps the reported names canonical.
|
|
38227
|
+
*
|
|
38228
|
+
* @param options - Raw runtime log content and the project directory names of the agent.
|
|
38229
|
+
* @returns Touched project names in the canonical spelling, ordered by first appearance.
|
|
38230
|
+
* @private internal utility of the agent-message runtime
|
|
38231
|
+
*/
|
|
38232
|
+
function resolveAgentMessageTouchedProjectNames(options) {
|
|
38233
|
+
const canonicalProjectNames = createCanonicalProjectNameLookup(options.knownProjectNames);
|
|
38234
|
+
if (canonicalProjectNames.size === 0) {
|
|
38235
|
+
return [];
|
|
38236
|
+
}
|
|
38237
|
+
const touchedProjectNames = new Set();
|
|
38238
|
+
for (const event of parseAgentMessageRuntimeLogEvents(options.logText)) {
|
|
38239
|
+
for (const toolInvocationText of resolveRuntimeLogEventToolInvocationTexts(event)) {
|
|
38240
|
+
for (const candidateProjectName of resolveCandidateProjectNames(toolInvocationText)) {
|
|
38241
|
+
const canonicalProjectName = canonicalProjectNames.get(candidateProjectName.toLowerCase());
|
|
38242
|
+
if (canonicalProjectName !== undefined) {
|
|
38243
|
+
touchedProjectNames.add(canonicalProjectName);
|
|
38244
|
+
}
|
|
38245
|
+
}
|
|
38246
|
+
}
|
|
38247
|
+
}
|
|
38248
|
+
return Array.from(touchedProjectNames);
|
|
38249
|
+
}
|
|
38250
|
+
/**
|
|
38251
|
+
* Indexes the existing project directory names by their lower-cased form.
|
|
38252
|
+
*
|
|
38253
|
+
* @param knownProjectNames - Project directory names of the agent.
|
|
38254
|
+
* @returns Lookup from the lower-cased name to its canonical spelling.
|
|
38255
|
+
* @private internal helper of `resolveAgentMessageTouchedProjectNames`
|
|
38256
|
+
*/
|
|
38257
|
+
function createCanonicalProjectNameLookup(knownProjectNames) {
|
|
38258
|
+
const canonicalProjectNames = new Map();
|
|
38259
|
+
for (const knownProjectName of knownProjectNames) {
|
|
38260
|
+
const normalizedProjectName = knownProjectName.trim();
|
|
38261
|
+
if (normalizedProjectName.length > 0) {
|
|
38262
|
+
canonicalProjectNames.set(normalizedProjectName.toLowerCase(), normalizedProjectName);
|
|
38263
|
+
}
|
|
38264
|
+
}
|
|
38265
|
+
return canonicalProjectNames;
|
|
38266
|
+
}
|
|
38267
|
+
/**
|
|
38268
|
+
* Collects the texts of every tool invocation described by one runtime log event.
|
|
38269
|
+
*
|
|
38270
|
+
* Claude Code reports tool invocations as `tool_use` content blocks, while Codex reports executed
|
|
38271
|
+
* commands and file changes as items — both carry the paths the harness worked with.
|
|
38272
|
+
*
|
|
38273
|
+
* @param event - One structured runtime log event.
|
|
38274
|
+
* @returns Texts that may contain project paths.
|
|
38275
|
+
* @private internal helper of `resolveAgentMessageTouchedProjectNames`
|
|
38276
|
+
*/
|
|
38277
|
+
function resolveRuntimeLogEventToolInvocationTexts(event) {
|
|
38278
|
+
var _a, _b, _c;
|
|
38279
|
+
const toolInvocationTexts = [];
|
|
38280
|
+
for (const contentBlock of ((_a = event.message) === null || _a === void 0 ? void 0 : _a.content) || []) {
|
|
38281
|
+
if (contentBlock.type === 'tool_use' && contentBlock.input) {
|
|
38282
|
+
toolInvocationTexts.push(serializeToolInput(contentBlock.input));
|
|
38283
|
+
}
|
|
38284
|
+
}
|
|
38285
|
+
if (typeof ((_b = event.item) === null || _b === void 0 ? void 0 : _b.command) === 'string') {
|
|
38286
|
+
toolInvocationTexts.push(event.item.command);
|
|
38287
|
+
}
|
|
38288
|
+
for (const fileChange of ((_c = event.item) === null || _c === void 0 ? void 0 : _c.changes) || []) {
|
|
38289
|
+
if (typeof fileChange.path === 'string') {
|
|
38290
|
+
toolInvocationTexts.push(fileChange.path);
|
|
38291
|
+
}
|
|
38292
|
+
}
|
|
38293
|
+
return toolInvocationTexts;
|
|
38294
|
+
}
|
|
38295
|
+
/**
|
|
38296
|
+
* Serializes one tool input payload into scannable text.
|
|
38297
|
+
*
|
|
38298
|
+
* @param toolInput - Tool arguments of one harness tool invocation.
|
|
38299
|
+
* @returns Serialized payload, or an empty string when it cannot be serialized.
|
|
38300
|
+
* @private internal helper of `resolveAgentMessageTouchedProjectNames`
|
|
38301
|
+
*/
|
|
38302
|
+
function serializeToolInput(toolInput) {
|
|
38303
|
+
try {
|
|
38304
|
+
return JSON.stringify(toolInput);
|
|
38305
|
+
}
|
|
38306
|
+
catch (_a) {
|
|
38307
|
+
return '';
|
|
38308
|
+
}
|
|
38309
|
+
}
|
|
38310
|
+
/**
|
|
38311
|
+
* Extracts every project directory name mentioned right after the agent `projects/` folder.
|
|
38312
|
+
*
|
|
38313
|
+
* @param toolInvocationText - One tool payload, command, or changed path.
|
|
38314
|
+
* @returns Candidate project directory names.
|
|
38315
|
+
* @private internal helper of `resolveAgentMessageTouchedProjectNames`
|
|
38316
|
+
*/
|
|
38317
|
+
function resolveCandidateProjectNames(toolInvocationText) {
|
|
38318
|
+
const candidateProjectNames = [];
|
|
38319
|
+
const projectPathRegex = new RegExp(TOUCHED_PROJECT_PATH_REGEX.source, TOUCHED_PROJECT_PATH_REGEX.flags);
|
|
38320
|
+
let projectPathMatch = projectPathRegex.exec(toolInvocationText);
|
|
38321
|
+
while (projectPathMatch !== null) {
|
|
38322
|
+
const candidateProjectName = projectPathMatch[1];
|
|
38323
|
+
if (candidateProjectName) {
|
|
38324
|
+
candidateProjectNames.push(candidateProjectName);
|
|
38325
|
+
}
|
|
38326
|
+
projectPathMatch = projectPathRegex.exec(toolInvocationText);
|
|
38327
|
+
}
|
|
38328
|
+
return candidateProjectNames;
|
|
38329
|
+
}
|
|
38330
|
+
|
|
38331
|
+
/**
|
|
38332
|
+
* Resolves which agent projects one answered message viewed or edited.
|
|
38333
|
+
*
|
|
38334
|
+
* The runtime log is deleted as soon as the harness run finishes, so this must be called while
|
|
38335
|
+
* the log still exists — the Agents Server later shows the reported projects as chips below the
|
|
38336
|
+
* answer. Reporting touched projects is best-effort telemetry: an unreadable log or projects
|
|
38337
|
+
* folder simply yields no projects instead of failing the already answered message.
|
|
38338
|
+
*
|
|
38339
|
+
* @param options - Agent folder path and the live runtime log path of the answered message.
|
|
38340
|
+
* @returns Touched project directory names, ordered by first appearance in the run.
|
|
38341
|
+
*/
|
|
38342
|
+
async function resolveTouchedAgentProjects(options) {
|
|
38343
|
+
const [logText, knownProjectNames] = await Promise.all([
|
|
38344
|
+
readOptionalTextFile(options.runtimeLogPath),
|
|
38345
|
+
listAgentProjectDirectoryNames(options.projectPath),
|
|
38346
|
+
]);
|
|
38347
|
+
return resolveAgentMessageTouchedProjectNames({ logText, knownProjectNames });
|
|
38348
|
+
}
|
|
38349
|
+
/**
|
|
38350
|
+
* Lists the direct project directories of one agent folder.
|
|
38351
|
+
*
|
|
38352
|
+
* @param projectPath - Absolute path of the local agent folder.
|
|
38353
|
+
* @returns Project directory names, or an empty list when the agent has no projects folder.
|
|
38354
|
+
*
|
|
38355
|
+
* @private helper of `resolveTouchedAgentProjects`
|
|
38356
|
+
*/
|
|
38357
|
+
async function listAgentProjectDirectoryNames(projectPath) {
|
|
38358
|
+
try {
|
|
38359
|
+
const projectsRootEntries = await readdir(join(projectPath, AGENT_PROJECTS_DIRECTORY_PATH), {
|
|
38360
|
+
withFileTypes: true,
|
|
38361
|
+
});
|
|
38362
|
+
return projectsRootEntries
|
|
38363
|
+
.filter((projectsRootEntry) => projectsRootEntry.isDirectory())
|
|
38364
|
+
.map((projectDirectoryEntry) => projectDirectoryEntry.name);
|
|
38365
|
+
}
|
|
38366
|
+
catch (_a) {
|
|
38367
|
+
return [];
|
|
38368
|
+
}
|
|
38369
|
+
}
|
|
38370
|
+
/**
|
|
38371
|
+
* Reads one text file and treats a missing runtime log as "nothing was recorded".
|
|
38372
|
+
*
|
|
38373
|
+
* @param filePath - Absolute path of the file to read.
|
|
38374
|
+
* @returns File content, or `null` when it cannot be read.
|
|
38375
|
+
*
|
|
38376
|
+
* @private helper of `resolveTouchedAgentProjects`
|
|
38377
|
+
*/
|
|
38378
|
+
async function readOptionalTextFile(filePath) {
|
|
38379
|
+
try {
|
|
38380
|
+
return await readFile(filePath, 'utf-8');
|
|
38381
|
+
}
|
|
38382
|
+
catch (_a) {
|
|
38383
|
+
return null;
|
|
38384
|
+
}
|
|
38385
|
+
}
|
|
38386
|
+
|
|
37625
38387
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
37626
38388
|
/**
|
|
37627
38389
|
* File suffix appended to one answered message book to store its run report sidecar.
|
|
@@ -37901,10 +38663,11 @@ async function runQueuedAgentMessage(options) {
|
|
|
37901
38663
|
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard, } = options;
|
|
37902
38664
|
const agentSystemMessage = await loadLocalAgentSystemMessage(projectPath);
|
|
37903
38665
|
const teamWorkspace = await loadAgentTeamConversationWorkspace(projectPath, queuedMessage);
|
|
38666
|
+
const plannedMessagesSidecar = await loadAgentPlannedMessagesSidecar(projectPath, queuedMessage);
|
|
37904
38667
|
const agentServerRuntimeApi = resolveAgentProjectRuntimePromptApi(projectPath);
|
|
37905
38668
|
const prompt = buildAgentMessagePrompt(queuedMessage.relativePath, agentSystemMessage, {
|
|
37906
38669
|
projectRuntimeApi: agentServerRuntimeApi,
|
|
37907
|
-
|
|
38670
|
+
plannedMessagesSidecar: plannedMessagesSidecar || undefined,
|
|
37908
38671
|
projectsUrlPath: resolveAgentProjectsUrlPath(projectPath),
|
|
37909
38672
|
teamWorkspace: teamWorkspace || undefined,
|
|
37910
38673
|
});
|
|
@@ -37925,21 +38688,29 @@ async function runQueuedAgentMessage(options) {
|
|
|
37925
38688
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(scriptPath);
|
|
37926
38689
|
}
|
|
37927
38690
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
|
|
38691
|
+
const executionStartedAt = new Date().toISOString();
|
|
37928
38692
|
let promptRunResult;
|
|
38693
|
+
let touchedProjectNames = [];
|
|
37929
38694
|
try {
|
|
37930
38695
|
try {
|
|
37931
|
-
promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) =>
|
|
37932
|
-
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
|
|
37937
|
-
|
|
37938
|
-
|
|
37939
|
-
|
|
37940
|
-
|
|
37941
|
-
|
|
37942
|
-
|
|
38696
|
+
promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) => {
|
|
38697
|
+
const runResult = await runPromptWithTestFeedback({
|
|
38698
|
+
runner,
|
|
38699
|
+
prompt,
|
|
38700
|
+
scriptPath,
|
|
38701
|
+
projectPath,
|
|
38702
|
+
promptLabel: queuedMessage.relativePath,
|
|
38703
|
+
logPath,
|
|
38704
|
+
preserveArtifactsOnSuccess: false,
|
|
38705
|
+
onAttemptStarted: (attemptCount) => {
|
|
38706
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setAttempt(attemptCount);
|
|
38707
|
+
},
|
|
38708
|
+
});
|
|
38709
|
+
// Note: The runtime log is deleted right after this handler, so the projects this
|
|
38710
|
+
// answer worked with must be resolved while the log still exists.
|
|
38711
|
+
touchedProjectNames = await resolveTouchedAgentProjects({ projectPath, runtimeLogPath: logPath });
|
|
38712
|
+
return runResult;
|
|
38713
|
+
}, { preserveArtifactsOnSuccess: false });
|
|
37943
38714
|
}
|
|
37944
38715
|
catch (error) {
|
|
37945
38716
|
throw withAgentWatchErrorContext(error, {
|
|
@@ -37953,6 +38724,7 @@ async function runQueuedAgentMessage(options) {
|
|
|
37953
38724
|
finally {
|
|
37954
38725
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
37955
38726
|
}
|
|
38727
|
+
const executionFinishedAt = new Date().toISOString();
|
|
37956
38728
|
await normalizeLineEndingsForAgentRound(projectPath, runOptions, roundChangedFilesSnapshot);
|
|
37957
38729
|
const finishedTeamWorkspace = await finalizeAgentTeamConversationWorkspace({
|
|
37958
38730
|
projectPath,
|
|
@@ -37971,6 +38743,11 @@ async function runQueuedAgentMessage(options) {
|
|
|
37971
38743
|
modelName: actualRunnerModel,
|
|
37972
38744
|
loginMethod: promptRunResult.loginMethod,
|
|
37973
38745
|
usage: promptRunResult.usage,
|
|
38746
|
+
executionTiming: {
|
|
38747
|
+
startedAt: executionStartedAt,
|
|
38748
|
+
finishedAt: executionFinishedAt,
|
|
38749
|
+
},
|
|
38750
|
+
...(touchedProjectNames.length === 0 ? {} : { touchedProjectNames }),
|
|
37974
38751
|
},
|
|
37975
38752
|
});
|
|
37976
38753
|
await commitAnsweredMessageIfEnabled({
|
|
@@ -39663,24 +40440,12 @@ const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
|
|
|
39663
40440
|
- @@@
|
|
39664
40441
|
- Keep in mind the DRY _(don't repeat yourself)_ principle.
|
|
39665
40442
|
- Do a proper analysis of the current functionality before you start implementing.
|
|
39666
|
-
- Add the changes into the [changelog](
|
|
40443
|
+
- Add the changes into the [changelog](CHANGELOG.md)
|
|
40444
|
+
- Update the [README](README.md) if needed.
|
|
40445
|
+
- Update the [AGENTS.md](AGENTS.md) for the next job to be done if it makes sense.
|
|
39667
40446
|
`),
|
|
39668
40447
|
isDefaultProjectTemplate: true,
|
|
39669
40448
|
},
|
|
39670
|
-
{
|
|
39671
|
-
id: 'agents-server',
|
|
39672
|
-
relativeFilePath: join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'agents-server.md'),
|
|
39673
|
-
slugPrefix: 'agents-server',
|
|
39674
|
-
content: spaceTrim$1(`
|
|
39675
|
-
- @@@
|
|
39676
|
-
- Keep in mind the DRY _(don't repeat yourself)_ principle.
|
|
39677
|
-
- Do a proper analysis of the current functionality before you start implementing.
|
|
39678
|
-
- You are working with the [Agents Server](apps/agents-server)
|
|
39679
|
-
- If you need to do the database migration, do it
|
|
39680
|
-
- Add the changes into the [changelog](changelog/_current-preversion.md)
|
|
39681
|
-
`),
|
|
39682
|
-
isDefaultProjectTemplate: false,
|
|
39683
|
-
},
|
|
39684
40449
|
];
|
|
39685
40450
|
/**
|
|
39686
40451
|
* Project-agnostic coder templates that `ptbk coder init` should materialize in any repository.
|
|
@@ -40772,6 +41537,10 @@ async function $applyHarnessInstallationStatus(status) {
|
|
|
40772
41537
|
console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is up to date.`));
|
|
40773
41538
|
return;
|
|
40774
41539
|
}
|
|
41540
|
+
if (installationState === 'installed') {
|
|
41541
|
+
console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is installed. Skipped checking for updates.`));
|
|
41542
|
+
return;
|
|
41543
|
+
}
|
|
40775
41544
|
if (installationState === 'unknown') {
|
|
40776
41545
|
console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is installed, the newest version could not be checked.`));
|
|
40777
41546
|
return;
|
|
@@ -40985,20 +41754,20 @@ function comparePrereleaseIdentifier(firstIdentifier, secondIdentifier) {
|
|
|
40985
41754
|
// Note: [🟡] Code for CLI npm package version comparison [isNpmPackageVersionOutdated](src/cli/cli-commands/common/npm/isNpmPackageVersionOutdated.ts) should never be published outside of `@promptbook/cli`
|
|
40986
41755
|
|
|
40987
41756
|
/**
|
|
40988
|
-
* Detects whether one CLI coding harness is installed globally and whether it is up to date.
|
|
41757
|
+
* Detects whether one CLI coding harness is installed globally and, when enabled, whether it is up to date.
|
|
40989
41758
|
*
|
|
40990
|
-
* Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and
|
|
41759
|
+
* Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and may query npm
|
|
40991
41760
|
*
|
|
40992
41761
|
* @private internal utility of `promptbookCli`
|
|
40993
41762
|
*/
|
|
40994
|
-
async function $checkHarnessInstallation(definition) {
|
|
41763
|
+
async function $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled = true) {
|
|
40995
41764
|
const [installedVersion, latestVersion] = await Promise.all([
|
|
40996
41765
|
$resolveInstalledHarnessVersion(definition),
|
|
40997
|
-
$resolveLatestNpmPackageVersion(definition.npmPackageName),
|
|
41766
|
+
isHarnessUpdateCheckEnabled ? $resolveLatestNpmPackageVersion(definition.npmPackageName) : Promise.resolve(null),
|
|
40998
41767
|
]);
|
|
40999
41768
|
return {
|
|
41000
41769
|
definition,
|
|
41001
|
-
installationState: resolveHarnessInstallationState(installedVersion, latestVersion),
|
|
41770
|
+
installationState: resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled),
|
|
41002
41771
|
installedVersion,
|
|
41003
41772
|
latestVersion,
|
|
41004
41773
|
};
|
|
@@ -41006,10 +41775,13 @@ async function $checkHarnessInstallation(definition) {
|
|
|
41006
41775
|
/**
|
|
41007
41776
|
* Derives the installation state from the detected versions.
|
|
41008
41777
|
*/
|
|
41009
|
-
function resolveHarnessInstallationState(installedVersion, latestVersion) {
|
|
41778
|
+
function resolveHarnessInstallationState(installedVersion, latestVersion, isHarnessUpdateCheckEnabled) {
|
|
41010
41779
|
if (installedVersion === null) {
|
|
41011
41780
|
return 'not-installed';
|
|
41012
41781
|
}
|
|
41782
|
+
if (!isHarnessUpdateCheckEnabled) {
|
|
41783
|
+
return 'installed';
|
|
41784
|
+
}
|
|
41013
41785
|
if (latestVersion === null) {
|
|
41014
41786
|
return 'unknown';
|
|
41015
41787
|
}
|
|
@@ -41020,8 +41792,8 @@ function resolveHarnessInstallationState(installedVersion, latestVersion) {
|
|
|
41020
41792
|
// Note: [🟡] Code for CLI harness installation check [$checkHarnessInstallation](src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts) should never be published outside of `@promptbook/cli`
|
|
41021
41793
|
|
|
41022
41794
|
/**
|
|
41023
|
-
* Checks that every given CLI coding harness is installed globally and up to date
|
|
41024
|
-
* install
|
|
41795
|
+
* Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
|
|
41796
|
+
* Offers to install missing harnesses and update outdated ones.
|
|
41025
41797
|
*
|
|
41026
41798
|
* Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
|
|
41027
41799
|
* are ignored so that every call site can pass its raw selection.
|
|
@@ -41030,13 +41802,13 @@ function resolveHarnessInstallationState(installedVersion, latestVersion) {
|
|
|
41030
41802
|
*
|
|
41031
41803
|
* @private internal utility of `promptbookCli`
|
|
41032
41804
|
*/
|
|
41033
|
-
async function $ensureHarnessInstallations(harnessNames) {
|
|
41805
|
+
async function $ensureHarnessInstallations(harnessNames, isHarnessUpdateCheckEnabled = true) {
|
|
41034
41806
|
const definitions = resolveCheckedHarnessDefinitions(harnessNames);
|
|
41035
41807
|
if (definitions.length === 0) {
|
|
41036
41808
|
return;
|
|
41037
41809
|
}
|
|
41038
41810
|
// Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
|
|
41039
|
-
const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition)));
|
|
41811
|
+
const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)));
|
|
41040
41812
|
for (const status of statuses) {
|
|
41041
41813
|
await $applyHarnessInstallationStatus(status);
|
|
41042
41814
|
}
|
|
@@ -41050,6 +41822,27 @@ function resolveCheckedHarnessDefinitions(harnessNames) {
|
|
|
41050
41822
|
}
|
|
41051
41823
|
// Note: [🟡] Code for CLI harness installation orchestration [$ensureHarnessInstallations](src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts) should never be published outside of `@promptbook/cli`
|
|
41052
41824
|
|
|
41825
|
+
/**
|
|
41826
|
+
* Registers the shared `--no-harness-update` option on a `ptbk coder` command that uses a coding harness.
|
|
41827
|
+
*
|
|
41828
|
+
* @private internal utility of `promptbookCli`
|
|
41829
|
+
*/
|
|
41830
|
+
function addHarnessUpdateOption(command) {
|
|
41831
|
+
command.option('--no-harness-update', 'Skip checking whether globally installed coding harnesses are up to date');
|
|
41832
|
+
}
|
|
41833
|
+
/**
|
|
41834
|
+
* Converts the Commander harness-update flag into the normalized harness-check option.
|
|
41835
|
+
*
|
|
41836
|
+
* @private internal utility of `promptbookCli`
|
|
41837
|
+
*/
|
|
41838
|
+
function normalizeHarnessUpdateCliOptions(cliOptions) {
|
|
41839
|
+
return {
|
|
41840
|
+
isHarnessUpdateCheckEnabled: cliOptions.harnessUpdate,
|
|
41841
|
+
};
|
|
41842
|
+
}
|
|
41843
|
+
// Note: [🟡] Code for CLI harness update options [harnessUpdateCliOptions](src/cli/cli-commands/common/harnessUpdateCliOptions.ts) should never be published outside of `@promptbook/cli`
|
|
41844
|
+
// Note: [💞] Ignore a discrepancy between file name and exported helper names
|
|
41845
|
+
|
|
41053
41846
|
/**
|
|
41054
41847
|
* Relative path to the Promptbook Coder quick-reference file initialized in project roots.
|
|
41055
41848
|
*
|
|
@@ -41612,14 +42405,16 @@ function $initializeCoderInitCommand(program) {
|
|
|
41612
42405
|
- CODING_AGENT_GIT_EMAIL
|
|
41613
42406
|
- CODING_AGENT_GIT_SIGNING_KEY
|
|
41614
42407
|
|
|
41615
|
-
Checks that the coding harnesses are installed globally and up to date:
|
|
42408
|
+
Checks that the coding harnesses are installed globally and up to date unless \`--no-harness-update\` is used:
|
|
41616
42409
|
${block(listCheckedHarnessLabels())}
|
|
41617
42410
|
|
|
41618
42411
|
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
41619
42412
|
`));
|
|
41620
42413
|
addCoderGitSyncOptions(command);
|
|
42414
|
+
addHarnessUpdateOption(command);
|
|
41621
42415
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
41622
42416
|
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions);
|
|
42417
|
+
const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
|
|
41623
42418
|
const projectPath = process.cwd();
|
|
41624
42419
|
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
41625
42420
|
const { $commitCoderChanges, $startCoderGitSync } = await Promise.resolve().then(function () { return coderGitSync; });
|
|
@@ -41632,7 +42427,7 @@ function $initializeCoderInitCommand(program) {
|
|
|
41632
42427
|
commitScope,
|
|
41633
42428
|
commitMessage: 'Initialize Promptbook Coder',
|
|
41634
42429
|
});
|
|
41635
|
-
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
|
|
42430
|
+
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, isHarnessUpdateCheckEnabled);
|
|
41636
42431
|
}));
|
|
41637
42432
|
}
|
|
41638
42433
|
/**
|
|
@@ -41671,13 +42466,16 @@ function $initializeCoderPingCommand(program) {
|
|
|
41671
42466
|
- Reports the answer of the harness, the response time and the reported usage
|
|
41672
42467
|
- Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
|
|
41673
42468
|
- Leaves the project exactly as it was — nothing is read, written, changed or committed
|
|
42469
|
+
- Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
|
|
41674
42470
|
- Use --no-ui to stream the raw harness output instead of only the compact result
|
|
41675
42471
|
`));
|
|
41676
42472
|
addPromptRunnerSelectionOptions(command);
|
|
42473
|
+
addHarnessUpdateOption(command);
|
|
41677
42474
|
addPromptRunnerRuntimeOptions(command);
|
|
41678
42475
|
command.action(handleActionErrors(async (cliOptions) => {
|
|
41679
42476
|
const runnerOptions = normalizePromptRunnerSelectionCliOptions(cliOptions, { isAgentRequired: true });
|
|
41680
|
-
|
|
42477
|
+
const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
|
|
42478
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
|
|
41681
42479
|
// Note: Import the ping dynamically to avoid loading heavy dependencies until needed
|
|
41682
42480
|
const { pingCoderHarness } = await Promise.resolve().then(function () { return pingCoderHarness$1; });
|
|
41683
42481
|
const { printCoderPingResult } = await Promise.resolve().then(function () { return printCoderPingResult$1; });
|
|
@@ -42168,7 +42966,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
42168
42966
|
- Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
|
|
42169
42967
|
- Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
|
|
42170
42968
|
- Optional --no-ui keeps plain streaming console output for logging and debugging
|
|
42171
|
-
- Checks that the selected harness is installed globally and up to date before the first prompt
|
|
42969
|
+
- Checks that the selected harness is installed globally and up to date before the first prompt unless --no-harness-update is used
|
|
42172
42970
|
- In interactive mode, checks local and global Promptbook CLI installations and offers to update them
|
|
42173
42971
|
- Supports GPG signing of commits
|
|
42174
42972
|
- Optional pre-coding test run that can stop or repair pre-existing failures
|
|
@@ -42178,6 +42976,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
42178
42976
|
`));
|
|
42179
42977
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
42180
42978
|
addPromptRunnerSelectionOptions(command);
|
|
42979
|
+
addHarnessUpdateOption(command);
|
|
42181
42980
|
command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
|
|
42182
42981
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
42183
42982
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
@@ -42219,6 +43018,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
42219
43018
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
42220
43019
|
isAgentRequired: !dryRun,
|
|
42221
43020
|
});
|
|
43021
|
+
const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
|
|
42222
43022
|
// [1] Parse the wait options and --no-auto:
|
|
42223
43023
|
// default: run automatically through the queue (no waiting between prompts)
|
|
42224
43024
|
// --no-auto: wait for user confirmation before each prompt (interactive mode)
|
|
@@ -42229,7 +43029,7 @@ function $initializeCoderRunCommand(program) {
|
|
|
42229
43029
|
if (await $ensurePromptbookCliInstallations()) {
|
|
42230
43030
|
return process.exit(0);
|
|
42231
43031
|
}
|
|
42232
|
-
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
43032
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
|
|
42233
43033
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
42234
43034
|
const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
|
|
42235
43035
|
const waitAfterError = parseOptionalWaitDuration(waitAfterErrorValue, DEFAULT_WAIT_AFTER_ERROR_MS$1);
|
|
@@ -42323,7 +43123,7 @@ function $initializeCoderServerCommand(program) {
|
|
|
42323
43123
|
|
|
42324
43124
|
Features:
|
|
42325
43125
|
- Runs the same prompt processing as \`ptbk coder run\`
|
|
42326
|
-
- Checks that the selected harness is installed globally and up to date on startup
|
|
43126
|
+
- Checks that the selected harness is installed globally and up to date on startup unless --no-harness-update is used
|
|
42327
43127
|
- Does not exit when all prompts are done; polls for new prompt files instead
|
|
42328
43128
|
- Serves a kanban board at http://localhost:<port> for visual progress tracking
|
|
42329
43129
|
- Allows editing prompt files directly from the browser (Trello-style)
|
|
@@ -42335,6 +43135,7 @@ function $initializeCoderServerCommand(program) {
|
|
|
42335
43135
|
.default(DEFAULT_CODER_SERVER_PORT));
|
|
42336
43136
|
command.option('--dry-run', 'Print unwritten prompts without executing', false);
|
|
42337
43137
|
addPromptRunnerSelectionOptions(command);
|
|
43138
|
+
addHarnessUpdateOption(command);
|
|
42338
43139
|
command.option('--agent <agent-book-path>', 'Path to a .book file whose compiled system message is prepended to each coding prompt');
|
|
42339
43140
|
command.option('--context <context-or-file>', 'Append extra instructions either inline or from a file path relative to the current project');
|
|
42340
43141
|
command.option('--test <test-command...>', 'Run a verification command after each prompt; quote it when the command itself contains top-level flags');
|
|
@@ -42366,7 +43167,8 @@ function $initializeCoderServerCommand(program) {
|
|
|
42366
43167
|
const runnerOptions = normalizePromptRunnerCliOptions(cliOptions, {
|
|
42367
43168
|
isAgentRequired: !dryRun,
|
|
42368
43169
|
});
|
|
42369
|
-
|
|
43170
|
+
const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(cliOptions);
|
|
43171
|
+
await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
|
|
42370
43172
|
// [1] Parse the wait options and --no-auto (same logic as `coder run`)
|
|
42371
43173
|
const waitForUser = !auto;
|
|
42372
43174
|
const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
|
|
@@ -44290,23 +45092,27 @@ async function locateAppOnMacOs({ macOsName, }) {
|
|
|
44290
45092
|
// TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
44291
45093
|
|
|
44292
45094
|
/**
|
|
44293
|
-
* Attempts to locate the specified application on a Windows system by searching common installation directories.
|
|
45095
|
+
* Attempts to locate the specified application on a Windows system by searching PATH and common installation directories.
|
|
44294
45096
|
* Returns the path to the executable if found, or null otherwise.
|
|
44295
45097
|
*
|
|
44296
45098
|
* @private within the repository
|
|
44297
45099
|
*/
|
|
44298
45100
|
async function locateAppOnWindows({ appName, windowsSuffix, }) {
|
|
44299
45101
|
try {
|
|
44300
|
-
const
|
|
45102
|
+
const EXECUTABLE_FILENAME = windowsSuffix.replace(/^.*[\\/]/, '');
|
|
45103
|
+
const PATH_EXECUTABLE_PATHS = (process.env.PATH || '')
|
|
45104
|
+
.split(delimiter)
|
|
45105
|
+
.filter(Boolean)
|
|
45106
|
+
.map((pathPrefix) => join(pathPrefix, EXECUTABLE_FILENAME));
|
|
45107
|
+
const INSTALLATION_EXECUTABLE_PATHS = [
|
|
44301
45108
|
process.env.LOCALAPPDATA,
|
|
44302
45109
|
join(process.env.LOCALAPPDATA || '', 'Programs'),
|
|
44303
45110
|
process.env.PROGRAMFILES,
|
|
44304
45111
|
process.env['PROGRAMFILES(X86)'],
|
|
44305
|
-
];
|
|
44306
|
-
for (const
|
|
44307
|
-
|
|
44308
|
-
|
|
44309
|
-
return path;
|
|
45112
|
+
].map((pathPrefix) => pathPrefix + windowsSuffix);
|
|
45113
|
+
for (const executablePath of [...PATH_EXECUTABLE_PATHS, ...INSTALLATION_EXECUTABLE_PATHS]) {
|
|
45114
|
+
if (await isExecutable(executablePath, $provideFilesystemForNode())) {
|
|
45115
|
+
return executablePath;
|
|
44310
45116
|
}
|
|
44311
45117
|
}
|
|
44312
45118
|
throw new Error(`Can not locate app ${appName} on Windows.`);
|
|
@@ -75699,6 +76505,10 @@ async function runTestBefore(options) {
|
|
|
75699
76505
|
* Constant for prompts dir.
|
|
75700
76506
|
*/
|
|
75701
76507
|
const PROMPTS_DIR$1 = join(process.cwd(), 'prompts');
|
|
76508
|
+
/**
|
|
76509
|
+
* Commit message for files changed by a successful or failed pre-coding test in repair mode.
|
|
76510
|
+
*/
|
|
76511
|
+
const PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE = 'test: Apply changes made by pre-coding tests';
|
|
75702
76512
|
/**
|
|
75703
76513
|
* Main entry point for running prompts with the selected agent.
|
|
75704
76514
|
*
|
|
@@ -76022,6 +76832,7 @@ async function runTestBeforeIfNeeded(options) {
|
|
|
76022
76832
|
});
|
|
76023
76833
|
await ensureWorkingTreeClean();
|
|
76024
76834
|
}
|
|
76835
|
+
const testBeforeCommitScope = await captureTestBeforeCommitScopeIfNeeded(runOptions);
|
|
76025
76836
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
|
|
76026
76837
|
const testBeforeResult = await runTestBefore({
|
|
76027
76838
|
testCommand: runOptions.testCommand,
|
|
@@ -76030,6 +76841,11 @@ async function runTestBeforeIfNeeded(options) {
|
|
|
76030
76841
|
}).finally(() => {
|
|
76031
76842
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
76032
76843
|
});
|
|
76844
|
+
await commitTestBeforeChangesIfNeeded({
|
|
76845
|
+
runOptions,
|
|
76846
|
+
testBeforeCommitScope,
|
|
76847
|
+
waitForRequestedPause,
|
|
76848
|
+
});
|
|
76033
76849
|
if (testBeforeResult.isPassed) {
|
|
76034
76850
|
return hasWaitedForStart;
|
|
76035
76851
|
}
|
|
@@ -76078,6 +76894,38 @@ async function runTestBeforeIfNeeded(options) {
|
|
|
76078
76894
|
});
|
|
76079
76895
|
return updatedHasWaitedForStart;
|
|
76080
76896
|
}
|
|
76897
|
+
/**
|
|
76898
|
+
* Captures the files present before a `yes-and-fix` pre-coding test, so only changes made by that test can be committed.
|
|
76899
|
+
*/
|
|
76900
|
+
async function captureTestBeforeCommitScopeIfNeeded(runOptions) {
|
|
76901
|
+
if (runOptions.testBefore !== 'yes-and-fix' || runOptions.noCommit) {
|
|
76902
|
+
return undefined;
|
|
76903
|
+
}
|
|
76904
|
+
return captureCoderCommitScope(process.cwd());
|
|
76905
|
+
}
|
|
76906
|
+
/**
|
|
76907
|
+
* Commits files changed by a `yes-and-fix` pre-coding test before the coder continues to a queued or repair prompt.
|
|
76908
|
+
*/
|
|
76909
|
+
async function commitTestBeforeChangesIfNeeded(options) {
|
|
76910
|
+
const { runOptions, testBeforeCommitScope, waitForRequestedPause } = options;
|
|
76911
|
+
if (!testBeforeCommitScope) {
|
|
76912
|
+
return;
|
|
76913
|
+
}
|
|
76914
|
+
const relevantPaths = await resolveCoderCommitScopePaths(testBeforeCommitScope);
|
|
76915
|
+
if (relevantPaths.length === 0) {
|
|
76916
|
+
return;
|
|
76917
|
+
}
|
|
76918
|
+
await waitForRequestedPause({
|
|
76919
|
+
checkpointLabel: 'committing changes made by pre-coding tests',
|
|
76920
|
+
phase: 'verifying',
|
|
76921
|
+
statusMessage: 'Committing changes made by pre-coding tests...',
|
|
76922
|
+
});
|
|
76923
|
+
await commitChanges(PRE_CODING_TEST_CHANGES_COMMIT_MESSAGE, {
|
|
76924
|
+
autoPush: runOptions.autoPush,
|
|
76925
|
+
projectPath: testBeforeCommitScope.projectPath,
|
|
76926
|
+
relevantPaths,
|
|
76927
|
+
});
|
|
76928
|
+
}
|
|
76081
76929
|
/**
|
|
76082
76930
|
* Creates a pause waiter that keeps the progress display and rich UI in sync.
|
|
76083
76931
|
*/
|