Files
EJClaw/src/message-runtime-prompts.ts
2026-04-07 19:27:03 +09:00

147 lines
4.1 KiB
TypeScript

import { buildArbiterContextPrompt } from './arbiter-context.js';
import { formatMessages } from './router.js';
import type { NewMessage, PairedTask, PairedTurnOutput } from './types.js';
function turnOutputsToMessages(
outputs: PairedTurnOutput[],
chatJid: string,
): NewMessage[] {
return outputs.map((turnOutput) => ({
id: `turn-${turnOutput.task_id}-${turnOutput.turn_number}`,
chat_jid: chatJid,
sender: turnOutput.role,
sender_name: turnOutput.role,
content: turnOutput.output_text,
timestamp: turnOutput.created_at,
is_bot_message: true as const,
is_from_me: false as const,
}));
}
function mergeHumanAndTurnOutputMessages(
chatJid: string,
humanMessages: NewMessage[],
turnOutputs: PairedTurnOutput[],
): NewMessage[] {
return [
...humanMessages,
...turnOutputsToMessages(turnOutputs, chatJid),
].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
}
export function buildPairedTurnPrompt(args: {
taskId: string;
chatJid: string;
timezone: string;
missedMessages: NewMessage[];
labeledFallbackMessages: NewMessage[];
turnOutputs: PairedTurnOutput[];
}): string {
if (args.turnOutputs.length === 0) {
return formatMessages(args.labeledFallbackMessages, args.timezone);
}
const humanMessages = args.missedMessages.filter(
(message) => !message.is_bot_message,
);
return formatMessages(
mergeHumanAndTurnOutputMessages(
args.chatJid,
humanMessages,
args.turnOutputs,
),
args.timezone,
);
}
export function buildReviewerPendingPrompt(args: {
chatJid: string;
timezone: string;
turnOutputs: PairedTurnOutput[];
recentHumanMessages: NewMessage[];
lastHumanMessage: string | null | undefined;
}): string {
if (args.turnOutputs.length > 0) {
return formatMessages(
mergeHumanAndTurnOutputMessages(
args.chatJid,
args.recentHumanMessages,
args.turnOutputs,
),
args.timezone,
);
}
if (!args.lastHumanMessage) {
return 'Review the latest owner changes in the workspace.';
}
return `User request:\n---\n${args.lastHumanMessage}\n---\n\nReview the latest owner changes in the workspace.`;
}
export function buildOwnerPendingPrompt(args: {
chatJid: string;
timezone: string;
turnOutputs: PairedTurnOutput[];
recentHumanMessages: NewMessage[];
lastHumanMessage: string | null | undefined;
}): string {
if (args.turnOutputs.length > 0) {
return formatMessages(
mergeHumanAndTurnOutputMessages(
args.chatJid,
args.recentHumanMessages,
args.turnOutputs,
),
args.timezone,
);
}
if (!args.lastHumanMessage) {
return 'Continue the owner turn using the latest reviewer or arbiter feedback.';
}
return `User request:\n---\n${args.lastHumanMessage}\n---\n\nContinue the owner turn using the latest reviewer or arbiter feedback.`;
}
export function buildArbiterPromptForTask(args: {
task: PairedTask;
chatJid: string;
timezone: string;
turnOutputs: PairedTurnOutput[];
recentMessages: NewMessage[];
labeledRecentMessages: NewMessage[];
}): string {
const messages =
args.turnOutputs.length > 0
? mergeHumanAndTurnOutputMessages(
args.chatJid,
args.recentMessages.filter((message) => !message.is_bot_message),
args.turnOutputs,
)
: args.labeledRecentMessages;
return buildArbiterContextPrompt({
chatJid: args.chatJid,
taskId: args.task.id,
roundTripCount: args.task.round_trip_count,
timezone: args.timezone,
messages,
});
}
export function buildFinalizePendingPrompt(args: {
turnOutputs: PairedTurnOutput[];
}): string {
const lastReviewerOutput = [...args.turnOutputs]
.reverse()
.find((output) => output.role === 'reviewer');
const reviewerSummary = lastReviewerOutput?.output_text
? `\n\nReviewer's final assessment:\n${lastReviewerOutput.output_text.slice(0, 2000)}`
: '';
return `The reviewer approved your work (DONE). Finalize and report the result.
If you intend to close this paired turn now, your first line must be DONE.
If your first line is DONE_WITH_CONCERNS, the system will reopen review instead of finishing.${reviewerSummary}`;
}