@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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { readFile, rm } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
+
import {
|
|
5
|
+
createAgentPlannedMessagesSidecarPath,
|
|
6
|
+
parseAgentPlannedMessagesSidecar,
|
|
7
|
+
type AgentPlannedMessageCommand,
|
|
8
|
+
} from '../../../../../src/book-3.0/AgentPlannedMessagesSidecar';
|
|
9
|
+
import {
|
|
10
|
+
AGENT_GOAL_CHAT_PLANNED_MESSAGE_ACTIONS,
|
|
11
|
+
type CancelAgentGoalChatPlannedMessageResult,
|
|
12
|
+
type SetAgentGoalChatPlannedMessageResult,
|
|
13
|
+
} from '../agentGoalChat/agentGoalChatPlannedMessageActions';
|
|
14
|
+
import type { UserChatJobRecord } from '../userChat/UserChatJobRecord';
|
|
15
|
+
import type { LocalUserChatJobMetadata } from './LocalUserChatJobMetadata';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Planned-message operations used while applying one sidecar.
|
|
19
|
+
*/
|
|
20
|
+
export type LocalAgentPlannedMessageActions = Pick<typeof AGENT_GOAL_CHAT_PLANNED_MESSAGE_ACTIONS, 'set' | 'cancel'>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One planned-message command that was applied together with its result.
|
|
24
|
+
*
|
|
25
|
+
* The command carries what the agent asked for (for example the requested delay) and the result
|
|
26
|
+
* carries what really happened, so the chat can show the wake-up below the message that planned it.
|
|
27
|
+
*/
|
|
28
|
+
export type AppliedAgentPlannedMessageCommand = {
|
|
29
|
+
readonly command: AgentPlannedMessageCommand;
|
|
30
|
+
readonly result: SetAgentGoalChatPlannedMessageResult | CancelAgentGoalChatPlannedMessageResult;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Applies every planned-message command a coding harness wrote while answering one message.
|
|
35
|
+
*
|
|
36
|
+
* The sidecar is consumed exactly once: it is removed afterwards so a replayed worker tick can never
|
|
37
|
+
* schedule the very same wake-up twice.
|
|
38
|
+
*
|
|
39
|
+
* @param options - Answered job, its agent folder path, and the local runner metadata of the message.
|
|
40
|
+
* @returns Planned-message commands that were applied successfully, in sidecar order.
|
|
41
|
+
*/
|
|
42
|
+
export async function applyLocalAgentPlannedMessageCommands(options: {
|
|
43
|
+
readonly job: Pick<UserChatJobRecord, 'id' | 'chatId' | 'agentPermanentId'>;
|
|
44
|
+
readonly agentDirectoryPath: string;
|
|
45
|
+
readonly metadata: LocalUserChatJobMetadata;
|
|
46
|
+
readonly actions?: LocalAgentPlannedMessageActions;
|
|
47
|
+
}): Promise<ReadonlyArray<AppliedAgentPlannedMessageCommand>> {
|
|
48
|
+
const sidecarPath = resolveLocalAgentPlannedMessagesSidecarPath(options.agentDirectoryPath, options.metadata);
|
|
49
|
+
const sidecarContent = await readOptionalTextFile(sidecarPath);
|
|
50
|
+
|
|
51
|
+
await removeLocalAgentPlannedMessagesSidecar(options.agentDirectoryPath, options.metadata);
|
|
52
|
+
|
|
53
|
+
if (sidecarContent === null) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const sidecar = parseAgentPlannedMessagesSidecar(sidecarContent);
|
|
58
|
+
|
|
59
|
+
if (!sidecar) {
|
|
60
|
+
console.warn('[local-chat-runner] planned_messages_sidecar_unreadable', {
|
|
61
|
+
chatId: options.job.chatId,
|
|
62
|
+
jobId: options.job.id,
|
|
63
|
+
});
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const actions = options.actions || AGENT_GOAL_CHAT_PLANNED_MESSAGE_ACTIONS;
|
|
68
|
+
const appliedCommands: Array<AppliedAgentPlannedMessageCommand> = [];
|
|
69
|
+
|
|
70
|
+
for (const command of sidecar.commands) {
|
|
71
|
+
const result = await applyLocalAgentPlannedMessageCommand({
|
|
72
|
+
command,
|
|
73
|
+
actions,
|
|
74
|
+
agentPermanentId: options.job.agentPermanentId,
|
|
75
|
+
job: options.job,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (result) {
|
|
79
|
+
appliedCommands.push({ command, result });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return appliedCommands;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Removes the planned-message sidecar of one queued message.
|
|
88
|
+
*
|
|
89
|
+
* @param agentDirectoryPath - Absolute path of the local agent folder.
|
|
90
|
+
* @param metadata - Local runner metadata of the queued message.
|
|
91
|
+
*/
|
|
92
|
+
export async function removeLocalAgentPlannedMessagesSidecar(
|
|
93
|
+
agentDirectoryPath: string,
|
|
94
|
+
metadata: LocalUserChatJobMetadata,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
await rm(resolveLocalAgentPlannedMessagesSidecarPath(agentDirectoryPath, metadata), { force: true }).catch(
|
|
97
|
+
() => undefined,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Dispatches one planned-message command through the shared planned-message actions.
|
|
103
|
+
*
|
|
104
|
+
* One rejected command never blocks the remaining ones, because the agent already answered and the
|
|
105
|
+
* harness owns this sidecar.
|
|
106
|
+
*
|
|
107
|
+
* @param options - Command, actions, and the agent whose goal chat owns the planned message.
|
|
108
|
+
* @returns Result of the applied command, or `null` when it was rejected.
|
|
109
|
+
*
|
|
110
|
+
* @private function of `applyLocalAgentPlannedMessageCommands`
|
|
111
|
+
*/
|
|
112
|
+
async function applyLocalAgentPlannedMessageCommand(options: {
|
|
113
|
+
readonly command: AgentPlannedMessageCommand;
|
|
114
|
+
readonly actions: LocalAgentPlannedMessageActions;
|
|
115
|
+
readonly agentPermanentId: string;
|
|
116
|
+
readonly job: Pick<UserChatJobRecord, 'id' | 'chatId'>;
|
|
117
|
+
}): Promise<AppliedAgentPlannedMessageCommand['result'] | null> {
|
|
118
|
+
try {
|
|
119
|
+
if (options.command.action === 'set') {
|
|
120
|
+
const plannedMessage = await options.actions.set({
|
|
121
|
+
agentPermanentId: options.agentPermanentId,
|
|
122
|
+
milliseconds: options.command.milliseconds,
|
|
123
|
+
message: options.command.message,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
console.info('[local-chat-runner]', 'planned_message_set', {
|
|
127
|
+
chatId: options.job.chatId,
|
|
128
|
+
jobId: options.job.id,
|
|
129
|
+
timeoutId: plannedMessage.timeoutId,
|
|
130
|
+
dueAt: plannedMessage.dueAt,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return plannedMessage;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const cancelledPlannedMessage = await options.actions.cancel({
|
|
137
|
+
agentPermanentId: options.agentPermanentId,
|
|
138
|
+
timeoutId: options.command.timeoutId,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
console.info('[local-chat-runner]', 'planned_message_cancel', {
|
|
142
|
+
chatId: options.job.chatId,
|
|
143
|
+
jobId: options.job.id,
|
|
144
|
+
timeoutId: cancelledPlannedMessage.timeoutId,
|
|
145
|
+
status: cancelledPlannedMessage.status,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return cancelledPlannedMessage.status === 'cancelled' ? cancelledPlannedMessage : null;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('[local-chat-runner]', 'planned_message_command_failed', {
|
|
151
|
+
chatId: options.job.chatId,
|
|
152
|
+
jobId: options.job.id,
|
|
153
|
+
action: options.command.action,
|
|
154
|
+
error: serializeError(error as Error),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Resolves the absolute planned-message sidecar path of one queued message.
|
|
163
|
+
*
|
|
164
|
+
* @param agentDirectoryPath - Absolute path of the local agent folder.
|
|
165
|
+
* @param metadata - Local runner metadata of the queued message.
|
|
166
|
+
* @returns Absolute sidecar path.
|
|
167
|
+
*
|
|
168
|
+
* @private function of `applyLocalAgentPlannedMessageCommands`
|
|
169
|
+
*/
|
|
170
|
+
function resolveLocalAgentPlannedMessagesSidecarPath(
|
|
171
|
+
agentDirectoryPath: string,
|
|
172
|
+
metadata: LocalUserChatJobMetadata,
|
|
173
|
+
): string {
|
|
174
|
+
return join(agentDirectoryPath, createAgentPlannedMessagesSidecarPath(metadata.fileName));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Reads one text file and treats a missing planned-message sidecar as absent.
|
|
179
|
+
*
|
|
180
|
+
* @param path - Absolute sidecar path.
|
|
181
|
+
* @returns File content, or `null` when the sidecar does not exist.
|
|
182
|
+
*
|
|
183
|
+
* @private function of `applyLocalAgentPlannedMessageCommands`
|
|
184
|
+
*/
|
|
185
|
+
async function readOptionalTextFile(path: string): Promise<string | null> {
|
|
186
|
+
try {
|
|
187
|
+
return await readFile(path, 'utf-8');
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (isFileNotFoundError(error)) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Returns true when one filesystem error indicates a missing path.
|
|
199
|
+
*
|
|
200
|
+
* @param error - Caught filesystem failure.
|
|
201
|
+
* @returns `true` for missing-path errors.
|
|
202
|
+
*
|
|
203
|
+
* @private function of `applyLocalAgentPlannedMessageCommands`
|
|
204
|
+
*/
|
|
205
|
+
function isFileNotFoundError(error: unknown): boolean {
|
|
206
|
+
return Boolean(
|
|
207
|
+
error &&
|
|
208
|
+
typeof error === 'object' &&
|
|
209
|
+
'code' in error &&
|
|
210
|
+
((error as { code?: string }).code === 'ENOENT' || (error as { code?: string }).code === 'ENOTDIR'),
|
|
211
|
+
);
|
|
212
|
+
}
|
package/apps/agents-server/src/utils/localChatRunner/prepareLocalAgentPlannedMessagesSidecar.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import {
|
|
4
|
+
createAgentPlannedMessagesSidecarContent,
|
|
5
|
+
createAgentPlannedMessagesSidecarPath,
|
|
6
|
+
type AgentPlannedMessageSnapshot,
|
|
7
|
+
type AgentPlannedMessagesSidecar,
|
|
8
|
+
} from '../../../../../src/book-3.0/AgentPlannedMessagesSidecar';
|
|
9
|
+
import { AGENT_GOAL_CHAT_PLANNED_MESSAGE_ACTIONS } from '../agentGoalChat/agentGoalChatPlannedMessageActions';
|
|
10
|
+
import type { UserChatJobRecord } from '../userChat/UserChatJobRecord';
|
|
11
|
+
import type { LocalAgentFolder } from './ensureLocalAgentFolder';
|
|
12
|
+
import type { LocalUserChatJobMetadata } from './LocalUserChatJobMetadata';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Writes the planned-message sidecar a coding harness uses to see and change its future wake-ups.
|
|
16
|
+
*
|
|
17
|
+
* The sidecar is the only planning channel of a locally executed turn: the harness never has to reach
|
|
18
|
+
* the Agents Server over the network, and a wake-up exists only when it was actually requested here.
|
|
19
|
+
* Planned messages repeat on their own, so an empty `commands` array keeps the current plan intact.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Answered job, its agent folder, and the local runner metadata of the queued message.
|
|
22
|
+
*/
|
|
23
|
+
export async function prepareLocalAgentPlannedMessagesSidecar(options: {
|
|
24
|
+
readonly job: Pick<UserChatJobRecord, 'agentPermanentId'>;
|
|
25
|
+
readonly agentFolder: LocalAgentFolder;
|
|
26
|
+
readonly metadata: LocalUserChatJobMetadata;
|
|
27
|
+
}): Promise<void> {
|
|
28
|
+
const sidecarPath = join(
|
|
29
|
+
options.agentFolder.directoryPath,
|
|
30
|
+
createAgentPlannedMessagesSidecarPath(options.metadata.fileName),
|
|
31
|
+
);
|
|
32
|
+
const plannedMessages = await AGENT_GOAL_CHAT_PLANNED_MESSAGE_ACTIONS.list({
|
|
33
|
+
agentPermanentId: options.job.agentPermanentId,
|
|
34
|
+
});
|
|
35
|
+
const sidecar: AgentPlannedMessagesSidecar = {
|
|
36
|
+
version: 1,
|
|
37
|
+
agentPermanentId: options.job.agentPermanentId,
|
|
38
|
+
currentPlannedMessages: plannedMessages.items.map(createLocalAgentPlannedMessageSnapshot),
|
|
39
|
+
commands: [],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
await mkdir(dirname(sidecarPath), { recursive: true });
|
|
43
|
+
await writeFile(sidecarPath, createAgentPlannedMessagesSidecarContent(sidecar), 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reduces one stored planned message to the fields a coding harness needs.
|
|
48
|
+
*
|
|
49
|
+
* @param plannedMessage - Planned message returned by the shared planned-message actions.
|
|
50
|
+
* @returns Snapshot written into the sidecar.
|
|
51
|
+
*
|
|
52
|
+
* @private function of `prepareLocalAgentPlannedMessagesSidecar`
|
|
53
|
+
*/
|
|
54
|
+
function createLocalAgentPlannedMessageSnapshot(plannedMessage: {
|
|
55
|
+
readonly timeoutId: string;
|
|
56
|
+
readonly dueAt: string;
|
|
57
|
+
readonly message: string | null;
|
|
58
|
+
readonly intervalMs: number | null;
|
|
59
|
+
}): AgentPlannedMessageSnapshot {
|
|
60
|
+
return {
|
|
61
|
+
timeoutId: plannedMessage.timeoutId,
|
|
62
|
+
dueAt: plannedMessage.dueAt,
|
|
63
|
+
message: plannedMessage.message,
|
|
64
|
+
intervalMs: plannedMessage.intervalMs,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Json } from '@/src/database/schema';
|
|
2
|
+
import type { ToolCall } from '@promptbook-local/types';
|
|
2
3
|
import { mkdir, readFile, rm, writeFile } from 'fs/promises';
|
|
3
4
|
import { join } from 'path';
|
|
4
5
|
import { parseAgentSource } from '../../../../../src/book-2.0/agent-source/parseAgentSource';
|
|
5
6
|
import {
|
|
6
7
|
buildAgentMessageRunReportPath,
|
|
7
8
|
parseAgentMessageRunReport,
|
|
9
|
+
type AgentMessageRunReport,
|
|
8
10
|
} from '../../../../../src/book-3.0/AgentMessageRunReport';
|
|
11
|
+
import { createAnsweredMessageChipToolCalls } from '../chatMessageChips/createAnsweredMessageChipToolCalls';
|
|
9
12
|
import { createUserChatJobFailureDetails } from '../userChat/createUserChatJobFailureDetails';
|
|
10
13
|
import { claimNextQueuedUserChatJob } from '../userChat/claimNextQueuedUserChatJob';
|
|
11
14
|
import { finalizeUserChatJob } from '../userChat/finalizeUserChatJob';
|
|
@@ -14,6 +17,7 @@ import { getUserChatJobById } from '../userChat/getUserChatJobById';
|
|
|
14
17
|
import { persistUserChatJobTerminalState } from '../userChat/persistUserChatJobTerminalState';
|
|
15
18
|
import { provideUserChatJobTable } from '../userChat/provideUserChatJobTable';
|
|
16
19
|
import { updateUserChatAssistantMessage } from '../userChat/updateUserChatAssistantMessage';
|
|
20
|
+
import { resolveUserChatJobExecutionTiming } from '../userChat/userChatJobRunReport';
|
|
17
21
|
import type { UserChatJobRecord } from '../userChat/UserChatJobRecord';
|
|
18
22
|
import type { UserChatJobRow } from '../userChat/UserChatJobRow';
|
|
19
23
|
import { createUserChatRunnerThreadMessages } from '../userChat/userChatMessageLifecycle';
|
|
@@ -32,6 +36,12 @@ import {
|
|
|
32
36
|
parseLocalChatFinishedMessageBook,
|
|
33
37
|
} from './localChatMessageBook';
|
|
34
38
|
import { LOCAL_USER_CHAT_JOB_ANSWER_TIMEOUT_MS, LOCAL_USER_CHAT_JOB_PROVIDER } from './localChatRunnerConstants';
|
|
39
|
+
import {
|
|
40
|
+
applyLocalAgentPlannedMessageCommands,
|
|
41
|
+
removeLocalAgentPlannedMessagesSidecar,
|
|
42
|
+
type AppliedAgentPlannedMessageCommand,
|
|
43
|
+
} from './applyLocalAgentPlannedMessageCommands';
|
|
44
|
+
import { prepareLocalAgentPlannedMessagesSidecar } from './prepareLocalAgentPlannedMessagesSidecar';
|
|
35
45
|
import {
|
|
36
46
|
ensureLocalAgentFolder,
|
|
37
47
|
loadLocalAgentSourceSnapshot,
|
|
@@ -191,6 +201,7 @@ async function enqueueLocalUserChatJobWithinTerminalCapture(
|
|
|
191
201
|
metadata,
|
|
192
202
|
primaryAgentName: agentSourceSnapshot.agentName,
|
|
193
203
|
});
|
|
204
|
+
await prepareLocalAgentPlannedMessagesSidecarIfPossible({ job, agentFolder, metadata });
|
|
194
205
|
await mkdir(join(agentFolder.directoryPath, 'messages', 'queued'), { recursive: true });
|
|
195
206
|
await writeFile(queuedMessagePath, createLocalChatQueuedMessageBook({ messages: threadMessages }), 'utf-8');
|
|
196
207
|
|
|
@@ -228,6 +239,22 @@ async function prepareLocalTeamConversationWorkspaceIfPossible(
|
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Prepares the planned-message sidecar without preventing an answer when planned messages cannot be read.
|
|
244
|
+
*/
|
|
245
|
+
async function prepareLocalAgentPlannedMessagesSidecarIfPossible(
|
|
246
|
+
options: Parameters<typeof prepareLocalAgentPlannedMessagesSidecar>[0],
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
try {
|
|
249
|
+
await prepareLocalAgentPlannedMessagesSidecar(options);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.warn('[local-chat-runner] planned_messages_sidecar_prepare_failed', {
|
|
252
|
+
agentPermanentId: options.job.agentPermanentId,
|
|
253
|
+
error,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
231
258
|
/**
|
|
232
259
|
* Synchronizes one already queued local job with message-folder output.
|
|
233
260
|
*/
|
|
@@ -245,7 +272,15 @@ async function synchronizeLocalUserChatJob(
|
|
|
245
272
|
});
|
|
246
273
|
|
|
247
274
|
if (content) {
|
|
248
|
-
await persistLocalUserChatJobRunReportIfPresent(job, agentDirectoryPath, metadata);
|
|
275
|
+
const runReport = await persistLocalUserChatJobRunReportIfPresent(job, agentDirectoryPath, metadata);
|
|
276
|
+
const executionTiming = resolveUserChatJobExecutionTiming(runReport);
|
|
277
|
+
// Note: Planned messages are applied before the answer becomes terminal, so a wake-up the
|
|
278
|
+
// agent announced in its answer is already stored when the answer becomes visible.
|
|
279
|
+
const appliedPlannedMessageCommands = await applyLocalAgentPlannedMessageCommandsIfPossible({
|
|
280
|
+
job,
|
|
281
|
+
agentDirectoryPath,
|
|
282
|
+
metadata,
|
|
283
|
+
});
|
|
249
284
|
const teamConversations = await parseFinishedLocalTeamConversations({
|
|
250
285
|
agentDirectoryPath,
|
|
251
286
|
metadata,
|
|
@@ -255,13 +290,20 @@ async function synchronizeLocalUserChatJob(
|
|
|
255
290
|
job,
|
|
256
291
|
conversations: teamConversations,
|
|
257
292
|
});
|
|
293
|
+
const chipToolCalls = await createAnsweredMessageChipToolCallsIfPossible({
|
|
294
|
+
job,
|
|
295
|
+
appliedPlannedMessageCommands,
|
|
296
|
+
touchedProjectNames: runReport?.touchedProjectNames || [],
|
|
297
|
+
});
|
|
298
|
+
const toolCalls = [...teamToolCalls, ...chipToolCalls];
|
|
258
299
|
await persistUserChatJobTerminalState({
|
|
259
300
|
job,
|
|
260
301
|
status: 'COMPLETED',
|
|
261
302
|
provider: LOCAL_USER_CHAT_JOB_PROVIDER,
|
|
262
303
|
content,
|
|
263
|
-
toolCalls:
|
|
264
|
-
|
|
304
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
305
|
+
executedAt: executionTiming?.executedAt,
|
|
306
|
+
generationDurationMs: executionTiming?.generationDurationMs,
|
|
265
307
|
});
|
|
266
308
|
return { didMutate: true, outcome: 'completed' };
|
|
267
309
|
}
|
|
@@ -278,26 +320,26 @@ async function synchronizeLocalUserChatJob(
|
|
|
278
320
|
return { didMutate: false, outcome: 'waiting' };
|
|
279
321
|
}
|
|
280
322
|
|
|
323
|
+
await removeLocalAgentPlannedMessagesSidecar(agentDirectoryPath, metadata);
|
|
281
324
|
await persistUserChatJobTerminalState({
|
|
282
325
|
job,
|
|
283
326
|
status: 'FAILED',
|
|
284
327
|
provider: LOCAL_USER_CHAT_JOB_PROVIDER,
|
|
285
328
|
failureReason,
|
|
286
329
|
failureDetails: createLocalUserChatJobFailureDetails(job, failureReason, 'localRunnerFailedFile'),
|
|
287
|
-
generationDurationMs: resolveLocalUserChatJobDurationMs(metadata.queuedAt),
|
|
288
330
|
});
|
|
289
331
|
return { didMutate: true, outcome: 'failed' };
|
|
290
332
|
}
|
|
291
333
|
|
|
292
334
|
if (isLocalUserChatJobTimedOut(metadata) && job.status !== 'FAILED') {
|
|
293
335
|
const failureReason = 'Local agent runner did not finish the queued message within 30 minutes.';
|
|
336
|
+
await removeLocalAgentPlannedMessagesSidecar(agentDirectoryPath, metadata);
|
|
294
337
|
await persistUserChatJobTerminalState({
|
|
295
338
|
job,
|
|
296
339
|
status: 'FAILED',
|
|
297
340
|
provider: LOCAL_USER_CHAT_JOB_PROVIDER,
|
|
298
341
|
failureReason,
|
|
299
342
|
failureDetails: createLocalUserChatJobFailureDetails(job, failureReason, 'localRunnerTimeout'),
|
|
300
|
-
generationDurationMs: resolveLocalUserChatJobDurationMs(metadata.queuedAt),
|
|
301
343
|
});
|
|
302
344
|
return { didMutate: true, outcome: 'failed' };
|
|
303
345
|
}
|
|
@@ -315,28 +357,34 @@ async function synchronizeLocalUserChatJob(
|
|
|
315
357
|
*
|
|
316
358
|
* The report is best-effort telemetry shown in the admin task details, so any read or
|
|
317
359
|
* persistence failure only logs a warning instead of failing the finished chat answer.
|
|
360
|
+
*
|
|
361
|
+
* @returns The report of the answered message, or `null` when the runner wrote none.
|
|
318
362
|
*/
|
|
319
363
|
async function persistLocalUserChatJobRunReportIfPresent(
|
|
320
364
|
job: UserChatJobRecord,
|
|
321
365
|
agentDirectoryPath: string,
|
|
322
366
|
metadata: LocalUserChatJobMetadata,
|
|
323
|
-
): Promise<
|
|
367
|
+
): Promise<AgentMessageRunReport | null> {
|
|
324
368
|
try {
|
|
325
369
|
const reportFileContent = await readOptionalTextFile(
|
|
326
370
|
join(agentDirectoryPath, buildAgentMessageRunReportPath(metadata.finishedPath)),
|
|
327
371
|
);
|
|
328
372
|
const report = reportFileContent === null ? null : parseAgentMessageRunReport(reportFileContent);
|
|
329
373
|
if (!report) {
|
|
330
|
-
return;
|
|
374
|
+
return null;
|
|
331
375
|
}
|
|
332
376
|
|
|
333
377
|
await persistUserChatJobRunReport(job, report);
|
|
378
|
+
|
|
379
|
+
return report;
|
|
334
380
|
} catch (error) {
|
|
335
381
|
console.warn('[local-chat-runner] run_report_sync_failed', {
|
|
336
382
|
chatId: job.chatId,
|
|
337
383
|
jobId: job.id,
|
|
338
384
|
error,
|
|
339
385
|
});
|
|
386
|
+
|
|
387
|
+
return null;
|
|
340
388
|
}
|
|
341
389
|
}
|
|
342
390
|
|
|
@@ -451,9 +499,61 @@ async function removeQueuedLocalMessageIfPresent(metadata: LocalUserChatJobMetad
|
|
|
451
499
|
return;
|
|
452
500
|
}
|
|
453
501
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}).catch(() => undefined);
|
|
502
|
+
const agentDirectoryPath = join(resolveLocalAgentRootPath(), metadata.agentDirectoryName);
|
|
503
|
+
|
|
504
|
+
await rm(join(agentDirectoryPath, metadata.queuedPath), { force: true }).catch(() => undefined);
|
|
505
|
+
await removeLocalAgentPlannedMessagesSidecar(agentDirectoryPath, metadata);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Applies planned-message commands without preventing an already answered message from completing.
|
|
510
|
+
*
|
|
511
|
+
* @returns The applied commands, or an empty list when the sidecar could not be applied.
|
|
512
|
+
*/
|
|
513
|
+
async function applyLocalAgentPlannedMessageCommandsIfPossible(
|
|
514
|
+
options: Parameters<typeof applyLocalAgentPlannedMessageCommands>[0],
|
|
515
|
+
): Promise<ReadonlyArray<AppliedAgentPlannedMessageCommand>> {
|
|
516
|
+
try {
|
|
517
|
+
return await applyLocalAgentPlannedMessageCommands(options);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.warn('[local-chat-runner] planned_messages_sidecar_apply_failed', {
|
|
520
|
+
chatId: options.job.chatId,
|
|
521
|
+
jobId: options.job.id,
|
|
522
|
+
error,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Builds the chips of one answered message without preventing it from completing.
|
|
531
|
+
*
|
|
532
|
+
* The chips only enrich the answer, so a failure to resolve them never hides an answer the
|
|
533
|
+
* agent already produced.
|
|
534
|
+
*
|
|
535
|
+
* @returns Tool calls rendered as chips below the answer, or an empty list when they failed.
|
|
536
|
+
*/
|
|
537
|
+
async function createAnsweredMessageChipToolCallsIfPossible(options: {
|
|
538
|
+
readonly job: UserChatJobRecord;
|
|
539
|
+
readonly appliedPlannedMessageCommands: ReadonlyArray<AppliedAgentPlannedMessageCommand>;
|
|
540
|
+
readonly touchedProjectNames: ReadonlyArray<string>;
|
|
541
|
+
}): Promise<ReadonlyArray<ToolCall>> {
|
|
542
|
+
try {
|
|
543
|
+
return await createAnsweredMessageChipToolCalls({
|
|
544
|
+
agentPermanentId: options.job.agentPermanentId,
|
|
545
|
+
appliedPlannedMessageCommands: options.appliedPlannedMessageCommands,
|
|
546
|
+
touchedProjectNames: options.touchedProjectNames,
|
|
547
|
+
});
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.warn('[local-chat-runner] message_chips_build_failed', {
|
|
550
|
+
chatId: options.job.chatId,
|
|
551
|
+
jobId: options.job.id,
|
|
552
|
+
error,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
457
557
|
}
|
|
458
558
|
|
|
459
559
|
/**
|
|
@@ -479,18 +579,6 @@ function isLocalUserChatJobTimedOut(metadata: LocalUserChatJobMetadata): boolean
|
|
|
479
579
|
return Number.isFinite(queuedAtMs) && Date.now() - queuedAtMs >= LOCAL_USER_CHAT_JOB_ANSWER_TIMEOUT_MS;
|
|
480
580
|
}
|
|
481
581
|
|
|
482
|
-
/**
|
|
483
|
-
* Resolves elapsed time since the local message was queued.
|
|
484
|
-
*/
|
|
485
|
-
function resolveLocalUserChatJobDurationMs(queuedAt: string): number | undefined {
|
|
486
|
-
const queuedAtMs = Date.parse(queuedAt);
|
|
487
|
-
if (!Number.isFinite(queuedAtMs)) {
|
|
488
|
-
return undefined;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return Math.max(0, Date.now() - queuedAtMs);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
582
|
/**
|
|
495
583
|
* Returns true when one filesystem error indicates a missing path.
|
|
496
584
|
*/
|
|
@@ -28,6 +28,10 @@ export async function persistUserChatJobTerminalState(options: {
|
|
|
28
28
|
provider?: string | null;
|
|
29
29
|
failureReason?: string | null;
|
|
30
30
|
failureDetails?: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Actual time at which an out-of-process runner finished this assistant message.
|
|
33
|
+
*/
|
|
34
|
+
executedAt?: NonNullable<ChatMessage['createdAt']>;
|
|
31
35
|
generationDurationMs?: number;
|
|
32
36
|
}): Promise<void> {
|
|
33
37
|
let updatedChat: Awaited<ReturnType<typeof updateUserChatAssistantMessage>> | null = null;
|
|
@@ -47,6 +51,7 @@ export async function persistUserChatJobTerminalState(options: {
|
|
|
47
51
|
ongoingToolCalls: undefined,
|
|
48
52
|
toolCalls: options.toolCalls ?? message.toolCalls,
|
|
49
53
|
completedToolCalls: options.toolCalls ?? message.completedToolCalls,
|
|
54
|
+
createdAt: options.executedAt ?? message.createdAt,
|
|
50
55
|
generationDurationMs: options.generationDurationMs ?? message.generationDurationMs,
|
|
51
56
|
progressCard: undefined,
|
|
52
57
|
availableTools: options.availableTools ?? message.availableTools,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Json } from '@/src/database/schema';
|
|
2
|
+
import type { ChatMessage } from '@promptbook-local/types';
|
|
2
3
|
import {
|
|
3
4
|
normalizeAgentMessageRunReport,
|
|
4
5
|
type AgentMessageRunReport,
|
|
@@ -42,6 +43,35 @@ export function withUserChatJobRunReport(
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Resolves display-ready execution timing from one runner-produced message report.
|
|
48
|
+
*
|
|
49
|
+
* The runner's timestamps describe only active harness work, unlike durable job timestamps which also include time
|
|
50
|
+
* waiting for the out-of-process runner to pick up a queued message.
|
|
51
|
+
*
|
|
52
|
+
* @returns The completed message timestamp and actual execution duration, or `null` for older reports without timing.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveUserChatJobExecutionTiming(report: AgentMessageRunReport | null): {
|
|
55
|
+
readonly executedAt: NonNullable<ChatMessage['createdAt']>;
|
|
56
|
+
readonly generationDurationMs: number;
|
|
57
|
+
} | null {
|
|
58
|
+
const executionTiming = report?.executionTiming;
|
|
59
|
+
if (!executionTiming) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const startedAtMilliseconds = Date.parse(executionTiming.startedAt);
|
|
64
|
+
const finishedAtMilliseconds = Date.parse(executionTiming.finishedAt);
|
|
65
|
+
if (!Number.isFinite(startedAtMilliseconds) || !Number.isFinite(finishedAtMilliseconds)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
executedAt: executionTiming.finishedAt as NonNullable<ChatMessage['createdAt']>,
|
|
71
|
+
generationDurationMs: Math.max(0, finishedAtMilliseconds - startedAtMilliseconds),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
45
75
|
/**
|
|
46
76
|
* Persists one runner-produced run report into the job parameters so task details can show it.
|
|
47
77
|
*/
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { spaceTrim } from 'spacetrim';
|
|
2
|
+
import { formatTimeoutDurationHuman } from '../../../../../src/book-components/Chat/utils/timeoutToolCallPresentation';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Builds the synthetic user-like message injected when a timeout elapses.
|
|
5
6
|
*
|
|
7
|
+
* A repeating planned message stays armed after every firing, so its wake-up says so: the agent
|
|
8
|
+
* only has to act on the message itself, not to plan the same wake-up again.
|
|
9
|
+
*
|
|
6
10
|
* @private internal utility of userChatTimeout
|
|
7
11
|
*/
|
|
8
12
|
export function createTimeoutWakeUpMessage(options: {
|
|
9
13
|
readonly timeoutId: string;
|
|
10
14
|
readonly durationMs: number;
|
|
15
|
+
readonly intervalMs?: number | null;
|
|
11
16
|
readonly message?: string | null;
|
|
12
17
|
}): string {
|
|
13
18
|
const message = options.message?.trim() || '';
|
|
19
|
+
const headline = options.intervalMs
|
|
20
|
+
? `⏱️ Planned message elapsed, repeating every ${formatTimeoutDurationHuman(options.intervalMs)}.`
|
|
21
|
+
: `⏱️ Timeout elapsed after ${options.durationMs}ms.`;
|
|
14
22
|
|
|
15
23
|
return spaceTrim(
|
|
16
24
|
(block) => `
|
|
17
|
-
|
|
25
|
+
${headline}
|
|
18
26
|
timeoutId: ${options.timeoutId}
|
|
19
27
|
${message ? block(message) : ''}
|
|
20
28
|
`,
|