@promptbook/cli 0.114.0-12 → 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/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/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/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/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 +639 -80
- 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/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/Chat/utils/agentProjectToolCall.d.ts +46 -0
- package/esm/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +24 -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-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/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/other/templates/getTemplatesPipelineCollection.ts +784 -738
- 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 +1 -0
- package/umd/index.umd.js +639 -80
- 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/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/Chat/utils/agentProjectToolCall.d.ts +46 -0
- package/umd/src/book-components/Chat/utils/timeoutToolCallPresentation.d.ts +24 -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
|
@@ -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
|
|
@@ -21882,7 +21878,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21882
21878
|
return agentBookStats.isFile();
|
|
21883
21879
|
}
|
|
21884
21880
|
catch (error) {
|
|
21885
|
-
if (isFileNotFoundError$
|
|
21881
|
+
if (isFileNotFoundError$7(error)) {
|
|
21886
21882
|
return false;
|
|
21887
21883
|
}
|
|
21888
21884
|
throw error;
|
|
@@ -21891,7 +21887,7 @@ async function isAgentRunnerProject(projectPath) {
|
|
|
21891
21887
|
/**
|
|
21892
21888
|
* Returns true when one filesystem error indicates a missing path.
|
|
21893
21889
|
*/
|
|
21894
|
-
function isFileNotFoundError$
|
|
21890
|
+
function isFileNotFoundError$7(error) {
|
|
21895
21891
|
return Boolean(error &&
|
|
21896
21892
|
typeof error === 'object' &&
|
|
21897
21893
|
'code' in error &&
|
|
@@ -21921,7 +21917,7 @@ async function readQueuedMessageDirectoryEntries(queuedMessagesDirectoryPath) {
|
|
|
21921
21917
|
return await readdir(queuedMessagesDirectoryPath, { withFileTypes: true });
|
|
21922
21918
|
}
|
|
21923
21919
|
catch (error) {
|
|
21924
|
-
if (isFileNotFoundError$
|
|
21920
|
+
if (isFileNotFoundError$6(error)) {
|
|
21925
21921
|
return [];
|
|
21926
21922
|
}
|
|
21927
21923
|
throw error;
|
|
@@ -21942,7 +21938,7 @@ function normalizeRelativePath$2(relativePath) {
|
|
|
21942
21938
|
/**
|
|
21943
21939
|
* Returns true when an error is a missing-path filesystem error.
|
|
21944
21940
|
*/
|
|
21945
|
-
function isFileNotFoundError$
|
|
21941
|
+
function isFileNotFoundError$6(error) {
|
|
21946
21942
|
return Boolean(error &&
|
|
21947
21943
|
typeof error === 'object' &&
|
|
21948
21944
|
'code' in error &&
|
|
@@ -22420,7 +22416,7 @@ async function runGitCommand(options) {
|
|
|
22420
22416
|
lastIndexLockState = await readGitIndexLockState(cwd, options.env);
|
|
22421
22417
|
if ((lastIndexLockState === null || lastIndexLockState === void 0 ? void 0 : lastIndexLockState.isStale) && !isStaleIndexLockRemoved) {
|
|
22422
22418
|
await unlink(lastIndexLockState.path).catch((unlinkError) => {
|
|
22423
|
-
if (isFileNotFoundError$
|
|
22419
|
+
if (isFileNotFoundError$5(unlinkError)) {
|
|
22424
22420
|
return;
|
|
22425
22421
|
}
|
|
22426
22422
|
throw unlinkError;
|
|
@@ -22481,7 +22477,7 @@ async function readGitIndexLockState(cwd, env) {
|
|
|
22481
22477
|
};
|
|
22482
22478
|
}
|
|
22483
22479
|
catch (error) {
|
|
22484
|
-
if (isFileNotFoundError$
|
|
22480
|
+
if (isFileNotFoundError$5(error)) {
|
|
22485
22481
|
return undefined;
|
|
22486
22482
|
}
|
|
22487
22483
|
throw error;
|
|
@@ -22538,7 +22534,7 @@ function formatDelay$1(delayMs) {
|
|
|
22538
22534
|
/**
|
|
22539
22535
|
* Detects missing-file errors while inspecting the lock file.
|
|
22540
22536
|
*/
|
|
22541
|
-
function isFileNotFoundError$
|
|
22537
|
+
function isFileNotFoundError$5(error) {
|
|
22542
22538
|
return Boolean(error &&
|
|
22543
22539
|
typeof error === 'object' &&
|
|
22544
22540
|
'code' in error &&
|
|
@@ -29614,7 +29610,7 @@ async function readWorkingTreeFileHash(projectPath, relativePath) {
|
|
|
29614
29610
|
return createHash('sha1').update(content).digest('hex');
|
|
29615
29611
|
}
|
|
29616
29612
|
catch (error) {
|
|
29617
|
-
if (isFileNotFoundError$
|
|
29613
|
+
if (isFileNotFoundError$4(error)) {
|
|
29618
29614
|
return null;
|
|
29619
29615
|
}
|
|
29620
29616
|
throw error;
|
|
@@ -29629,7 +29625,7 @@ function normalizeGitFilePath(filePath) {
|
|
|
29629
29625
|
/**
|
|
29630
29626
|
* Returns true when an error is a missing-file filesystem error.
|
|
29631
29627
|
*/
|
|
29632
|
-
function isFileNotFoundError$
|
|
29628
|
+
function isFileNotFoundError$4(error) {
|
|
29633
29629
|
return Boolean(error &&
|
|
29634
29630
|
typeof error === 'object' &&
|
|
29635
29631
|
'code' in error &&
|
|
@@ -30622,7 +30618,7 @@ function buildAgentProjectsPromptSection(options = {}) {
|
|
|
30622
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.
|
|
30623
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.
|
|
30624
30620
|
- A project can be a git repository — you can run \`git init\` inside a project folder and commit your work there.
|
|
30625
|
-
- 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.
|
|
30626
30622
|
|
|
30627
30623
|
${block(buildProjectReferenceInstructions(projectsUrlPath))}
|
|
30628
30624
|
${block(buildProjectRuntimeInstructions(options.projectRuntimeApi))}
|
|
@@ -30678,55 +30674,166 @@ function buildProjectRuntimeInstructions(projectRuntimeApi) {
|
|
|
30678
30674
|
}
|
|
30679
30675
|
|
|
30680
30676
|
/**
|
|
30681
|
-
*
|
|
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
|
|
30704
|
+
*/
|
|
30705
|
+
const DAY_IN_SECONDS = HOURS_IN_DAY * HOUR_IN_SECONDS;
|
|
30706
|
+
/**
|
|
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
|
|
30682
30716
|
*/
|
|
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
|
+
}
|
|
30683
30742
|
/**
|
|
30684
|
-
*
|
|
30743
|
+
* Formats one pluralized timeout unit.
|
|
30744
|
+
*
|
|
30745
|
+
* @private internal timeout-chat helper
|
|
30685
30746
|
*/
|
|
30686
|
-
|
|
30747
|
+
function formatTimeoutUnit(value, unit) {
|
|
30748
|
+
return `${value} ${unit}${value === 1 ? '' : 's'}`;
|
|
30749
|
+
}
|
|
30750
|
+
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
30687
30751
|
|
|
30688
30752
|
/**
|
|
30689
30753
|
* Builds the planned-message instructions available to an Agents Server-managed coding agent.
|
|
30690
30754
|
*
|
|
30691
|
-
* Planned messages always target the agent's singleton goal chat, regardless of the chat in which
|
|
30692
|
-
*
|
|
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.
|
|
30693
30762
|
*/
|
|
30694
|
-
function buildAgentGoalChatPromptSection(
|
|
30695
|
-
if (!
|
|
30763
|
+
function buildAgentGoalChatPromptSection(sidecar) {
|
|
30764
|
+
if (!sidecar) {
|
|
30696
30765
|
return '';
|
|
30697
30766
|
}
|
|
30698
|
-
return spaceTrim$1(`
|
|
30699
|
-
|
|
30767
|
+
return spaceTrim$1((block) => `
|
|
30768
|
+
## Planned goal-chat messages
|
|
30700
30769
|
|
|
30701
|
-
|
|
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.
|
|
30702
30771
|
|
|
30703
|
-
|
|
30704
|
-
- 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.
|
|
30705
|
-
- Use \`cancel_timeout\` with the returned \`timeoutId\` when a planned message is no longer useful.
|
|
30706
|
-
- 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))}
|
|
30707
30773
|
|
|
30708
|
-
|
|
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.
|
|
30709
30775
|
|
|
30710
|
-
|
|
30711
|
-
|
|
30712
|
-
|
|
30713
|
-
|
|
30714
|
-
|
|
30715
|
-
|
|
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:
|
|
30716
30800
|
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
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
|
+
}
|
|
30722
30820
|
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30727
|
-
|
|
30728
|
-
|
|
30729
|
-
|
|
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';
|
|
30730
30837
|
}
|
|
30731
30838
|
|
|
30732
30839
|
// Note: [💞] This file defines the shared TEAM workspace convention rather than one standalone entity.
|
|
@@ -30760,9 +30867,7 @@ const AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME = 'teammates';
|
|
|
30760
30867
|
* @private internal convention shared by the Agents Server and agent-folder runner
|
|
30761
30868
|
*/
|
|
30762
30869
|
function createAgentTeamConversationWorkspaceDirectoryName(messageFileName) {
|
|
30763
|
-
|
|
30764
|
-
const normalizedBaseName = rawBaseName.replace(/[^A-Za-z0-9._-]+/gu, '-').replace(/^[._-]+|[._-]+$/gu, '');
|
|
30765
|
-
return normalizedBaseName || 'message';
|
|
30870
|
+
return createAgentMessageSidecarBaseName(messageFileName);
|
|
30766
30871
|
}
|
|
30767
30872
|
/**
|
|
30768
30873
|
* Creates the relative active workspace path for one queued message.
|
|
@@ -30832,8 +30937,8 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30832
30937
|
return '';
|
|
30833
30938
|
}
|
|
30834
30939
|
const teammateSections = workspace.manifest.teammates.map((teammate) => {
|
|
30835
|
-
const sourcePath = toPromptPath(join(workspace.relativeWorkspacePath, AGENT_TEAMMATE_SOURCES_DIRECTORY_NAME, teammate.sourceFileName));
|
|
30836
|
-
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`));
|
|
30837
30942
|
const instructions = teammate.instructions.trim() || 'No additional TEAM instructions were provided.';
|
|
30838
30943
|
return spaceTrim$1(`
|
|
30839
30944
|
- ${teammate.agentName}
|
|
@@ -30851,7 +30956,7 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30851
30956
|
|
|
30852
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.
|
|
30853
30958
|
- The primary agent remains responsible for the final user-facing \`MESSAGE @Agent\` answer.
|
|
30854
|
-
- 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)}\`.
|
|
30855
30960
|
- Name each transcript \`<teammate permanent id>--<sequence>.book\`, for example \`${workspace.manifest.teammates[0].permanentId}--01.book\`.
|
|
30856
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.
|
|
30857
30962
|
`);
|
|
@@ -30859,7 +30964,7 @@ function buildAgentTeamPromptSection(workspace) {
|
|
|
30859
30964
|
/**
|
|
30860
30965
|
* Converts a filesystem-relative path into the portable path notation used in prompts.
|
|
30861
30966
|
*/
|
|
30862
|
-
function toPromptPath(path) {
|
|
30967
|
+
function toPromptPath$1(path) {
|
|
30863
30968
|
return path.replace(/\\/gu, '/');
|
|
30864
30969
|
}
|
|
30865
30970
|
|
|
@@ -30872,7 +30977,7 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30872
30977
|
|
|
30873
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\`.
|
|
30874
30979
|
- Only change the queued message file by appending one new \`MESSAGE @Agent\` block
|
|
30875
|
-
${block(buildAllowedFileChangesPromptLine(options
|
|
30980
|
+
${block(buildAllowedFileChangesPromptLine(options))}
|
|
30876
30981
|
|
|
30877
30982
|
## Rules for the answering
|
|
30878
30983
|
|
|
@@ -30908,7 +31013,7 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30908
31013
|
|
|
30909
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.
|
|
30910
31015
|
|
|
30911
|
-
${block(buildAgentGoalChatPromptSection(options.
|
|
31016
|
+
${block(buildAgentGoalChatPromptSection(options.plannedMessagesSidecar))}
|
|
30912
31017
|
|
|
30913
31018
|
${block(buildAgentProjectsPromptSection(options))}
|
|
30914
31019
|
|
|
@@ -30922,11 +31027,30 @@ function buildAgentMessagePrompt(messageRelativePath, agentSystemMessage, option
|
|
|
30922
31027
|
/**
|
|
30923
31028
|
* Explains the exact files that one coding harness may change for a user turn.
|
|
30924
31029
|
*/
|
|
30925
|
-
function buildAllowedFileChangesPromptLine(
|
|
30926
|
-
|
|
30927
|
-
|
|
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];
|
|
30928
31046
|
}
|
|
30929
|
-
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, '/');
|
|
30930
31054
|
}
|
|
30931
31055
|
|
|
30932
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"}];
|
|
@@ -37680,12 +37804,12 @@ async function finalizeAgentTeamConversationWorkspace(options) {
|
|
|
37680
37804
|
await rm(finishedWorkspacePath, { recursive: true, force: true });
|
|
37681
37805
|
await mkdir(finishedWorkspacePath, { recursive: true });
|
|
37682
37806
|
const relativePaths = [
|
|
37683
|
-
toPortablePath$
|
|
37807
|
+
toPortablePath$2(join(finishedRelativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME)),
|
|
37684
37808
|
];
|
|
37685
37809
|
await rename(activeManifestPath, join(finishedWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME));
|
|
37686
37810
|
for (const transcriptFileName of transcriptFileNames) {
|
|
37687
37811
|
await rename(join(activeWorkspacePath, transcriptFileName), join(finishedWorkspacePath, transcriptFileName));
|
|
37688
|
-
relativePaths.push(toPortablePath$
|
|
37812
|
+
relativePaths.push(toPortablePath$2(join(finishedRelativeWorkspacePath, transcriptFileName)));
|
|
37689
37813
|
}
|
|
37690
37814
|
await rm(activeWorkspacePath, { recursive: true, force: true });
|
|
37691
37815
|
return { relativePaths };
|
|
@@ -37702,7 +37826,7 @@ async function listAgentTeamTranscriptFileNames(activeWorkspacePath) {
|
|
|
37702
37826
|
.sort((firstFileName, secondFileName) => firstFileName.localeCompare(secondFileName));
|
|
37703
37827
|
}
|
|
37704
37828
|
catch (error) {
|
|
37705
|
-
if (isFileNotFoundError$
|
|
37829
|
+
if (isFileNotFoundError$3(error)) {
|
|
37706
37830
|
return [];
|
|
37707
37831
|
}
|
|
37708
37832
|
throw error;
|
|
@@ -37717,7 +37841,7 @@ async function isExistingPath$1(path) {
|
|
|
37717
37841
|
return true;
|
|
37718
37842
|
}
|
|
37719
37843
|
catch (error) {
|
|
37720
|
-
if (isFileNotFoundError$
|
|
37844
|
+
if (isFileNotFoundError$3(error)) {
|
|
37721
37845
|
return false;
|
|
37722
37846
|
}
|
|
37723
37847
|
throw error;
|
|
@@ -37726,11 +37850,204 @@ async function isExistingPath$1(path) {
|
|
|
37726
37850
|
/**
|
|
37727
37851
|
* Converts a filesystem-relative path into a portable Git path.
|
|
37728
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
|
+
*/
|
|
37729
38043
|
function toPortablePath$1(path) {
|
|
37730
38044
|
return path.replace(/\\/gu, '/');
|
|
37731
38045
|
}
|
|
37732
38046
|
/**
|
|
37733
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.
|
|
37734
38051
|
*/
|
|
37735
38052
|
function isFileNotFoundError$2(error) {
|
|
37736
38053
|
return Boolean(error &&
|
|
@@ -37745,7 +38062,7 @@ function isFileNotFoundError$2(error) {
|
|
|
37745
38062
|
async function loadAgentTeamConversationWorkspace(projectPath, queuedMessage) {
|
|
37746
38063
|
const relativeWorkspacePath = createAgentTeamConversationWorkspacePath(queuedMessage.fileName);
|
|
37747
38064
|
const manifestPath = join(projectPath, relativeWorkspacePath, AGENT_TEAM_CONVERSATION_MANIFEST_FILE_NAME);
|
|
37748
|
-
const manifestContent = await readOptionalTextFile(manifestPath);
|
|
38065
|
+
const manifestContent = await readOptionalTextFile$1(manifestPath);
|
|
37749
38066
|
if (manifestContent === null) {
|
|
37750
38067
|
return null;
|
|
37751
38068
|
}
|
|
@@ -37773,7 +38090,7 @@ function parseAgentTeamConversationWorkspaceManifest(manifestContent) {
|
|
|
37773
38090
|
/**
|
|
37774
38091
|
* Reads one text file and treats a missing team workspace as absent.
|
|
37775
38092
|
*/
|
|
37776
|
-
async function readOptionalTextFile(path) {
|
|
38093
|
+
async function readOptionalTextFile$1(path) {
|
|
37777
38094
|
try {
|
|
37778
38095
|
return await readFile(path, 'utf-8');
|
|
37779
38096
|
}
|
|
@@ -37840,6 +38157,233 @@ function resolveAgentProjectsUrlPath(projectPath) {
|
|
|
37840
38157
|
return `/agents/${encodeURIComponent(agentId)}/projects`;
|
|
37841
38158
|
}
|
|
37842
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
|
+
|
|
37843
38387
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
37844
38388
|
/**
|
|
37845
38389
|
* File suffix appended to one answered message book to store its run report sidecar.
|
|
@@ -38119,10 +38663,11 @@ async function runQueuedAgentMessage(options) {
|
|
|
38119
38663
|
const { projectPath, options: runOptions, runner, actualRunnerModel, queuedMessage, uiHandle, isSharedDashboard, } = options;
|
|
38120
38664
|
const agentSystemMessage = await loadLocalAgentSystemMessage(projectPath);
|
|
38121
38665
|
const teamWorkspace = await loadAgentTeamConversationWorkspace(projectPath, queuedMessage);
|
|
38666
|
+
const plannedMessagesSidecar = await loadAgentPlannedMessagesSidecar(projectPath, queuedMessage);
|
|
38122
38667
|
const agentServerRuntimeApi = resolveAgentProjectRuntimePromptApi(projectPath);
|
|
38123
38668
|
const prompt = buildAgentMessagePrompt(queuedMessage.relativePath, agentSystemMessage, {
|
|
38124
38669
|
projectRuntimeApi: agentServerRuntimeApi,
|
|
38125
|
-
|
|
38670
|
+
plannedMessagesSidecar: plannedMessagesSidecar || undefined,
|
|
38126
38671
|
projectsUrlPath: resolveAgentProjectsUrlPath(projectPath),
|
|
38127
38672
|
teamWorkspace: teamWorkspace || undefined,
|
|
38128
38673
|
});
|
|
@@ -38143,21 +38688,29 @@ async function runQueuedAgentMessage(options) {
|
|
|
38143
38688
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentScriptPath(scriptPath);
|
|
38144
38689
|
}
|
|
38145
38690
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.startCapturingAgentOutput();
|
|
38691
|
+
const executionStartedAt = new Date().toISOString();
|
|
38146
38692
|
let promptRunResult;
|
|
38693
|
+
let touchedProjectNames = [];
|
|
38147
38694
|
try {
|
|
38148
38695
|
try {
|
|
38149
|
-
promptRunResult = await withPromptRuntimeLog(scriptPath, async (logPath) =>
|
|
38150
|
-
|
|
38151
|
-
|
|
38152
|
-
|
|
38153
|
-
|
|
38154
|
-
|
|
38155
|
-
|
|
38156
|
-
|
|
38157
|
-
|
|
38158
|
-
|
|
38159
|
-
|
|
38160
|
-
|
|
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 });
|
|
38161
38714
|
}
|
|
38162
38715
|
catch (error) {
|
|
38163
38716
|
throw withAgentWatchErrorContext(error, {
|
|
@@ -38171,6 +38724,7 @@ async function runQueuedAgentMessage(options) {
|
|
|
38171
38724
|
finally {
|
|
38172
38725
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.stopCapturingAgentOutput();
|
|
38173
38726
|
}
|
|
38727
|
+
const executionFinishedAt = new Date().toISOString();
|
|
38174
38728
|
await normalizeLineEndingsForAgentRound(projectPath, runOptions, roundChangedFilesSnapshot);
|
|
38175
38729
|
const finishedTeamWorkspace = await finalizeAgentTeamConversationWorkspace({
|
|
38176
38730
|
projectPath,
|
|
@@ -38189,6 +38743,11 @@ async function runQueuedAgentMessage(options) {
|
|
|
38189
38743
|
modelName: actualRunnerModel,
|
|
38190
38744
|
loginMethod: promptRunResult.loginMethod,
|
|
38191
38745
|
usage: promptRunResult.usage,
|
|
38746
|
+
executionTiming: {
|
|
38747
|
+
startedAt: executionStartedAt,
|
|
38748
|
+
finishedAt: executionFinishedAt,
|
|
38749
|
+
},
|
|
38750
|
+
...(touchedProjectNames.length === 0 ? {} : { touchedProjectNames }),
|
|
38192
38751
|
},
|
|
38193
38752
|
});
|
|
38194
38753
|
await commitAnsweredMessageIfEnabled({
|