fix: keep paired review prompts output-only
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildArbiterPromptForTask,
|
||||
buildFinalizePendingPrompt,
|
||||
buildOwnerPendingPrompt,
|
||||
buildPairedTurnPrompt,
|
||||
buildReviewerPendingPrompt,
|
||||
} from './message-runtime-prompts.js';
|
||||
import type { NewMessage, PairedTurnOutput } from './types.js';
|
||||
import type { NewMessage, PairedTask, PairedTurnOutput } from './types.js';
|
||||
|
||||
const CARRY_FORWARD_MARKER =
|
||||
'[Carried forward context from the previous task: latest owner final]';
|
||||
@@ -27,6 +28,7 @@ function makeHumanMessage(content: string): NewMessage {
|
||||
function makeTurnOutput(
|
||||
outputText: string,
|
||||
role: PairedTurnOutput['role'] = 'owner',
|
||||
overrides: Partial<PairedTurnOutput> = {},
|
||||
): PairedTurnOutput {
|
||||
return {
|
||||
id: 1,
|
||||
@@ -35,6 +37,37 @@ function makeTurnOutput(
|
||||
role,
|
||||
output_text: outputText,
|
||||
created_at: '2026-04-20T00:59:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeTask(overrides: Partial<PairedTask> = {}): PairedTask {
|
||||
return {
|
||||
id: 'task-1',
|
||||
chat_jid: 'group@test',
|
||||
group_folder: 'group',
|
||||
owner_service_id: 'codex-main',
|
||||
reviewer_service_id: 'claude',
|
||||
owner_agent_type: 'codex',
|
||||
reviewer_agent_type: 'claude-code',
|
||||
arbiter_agent_type: 'codex',
|
||||
title: null,
|
||||
source_ref: null,
|
||||
plan_notes: null,
|
||||
review_requested_at: null,
|
||||
round_trip_count: 1,
|
||||
owner_failure_count: 0,
|
||||
owner_step_done_streak: 0,
|
||||
finalize_step_done_count: 0,
|
||||
task_done_then_user_reopen_count: 0,
|
||||
empty_step_done_streak: 0,
|
||||
status: 'review_ready',
|
||||
arbiter_verdict: null,
|
||||
arbiter_requested_at: null,
|
||||
completion_reason: null,
|
||||
created_at: '2026-04-20T00:58:00.000Z',
|
||||
updated_at: '2026-04-20T00:59:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,6 +112,26 @@ describe('message-runtime-prompts carry-forward guidance', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps reviewer pending prompts output-only when current task outputs exist', () => {
|
||||
const prompt = buildReviewerPendingPrompt({
|
||||
chatJid: 'group@test',
|
||||
timezone: 'UTC',
|
||||
turnOutputs: [
|
||||
makeTurnOutput('TASK_DONE\n현재 owner 결과', 'owner', {
|
||||
turn_number: 1,
|
||||
created_at: '2026-04-20T02:00:00.000Z',
|
||||
}),
|
||||
],
|
||||
recentHumanMessages: [
|
||||
makeHumanMessage('과거 요청을 다시 기준으로 보면 안 됨'),
|
||||
],
|
||||
lastHumanMessage: '과거 요청을 다시 기준으로 보면 안 됨',
|
||||
});
|
||||
|
||||
expect(prompt).toContain('현재 owner 결과');
|
||||
expect(prompt).not.toContain('과거 요청을 다시 기준으로 보면 안 됨');
|
||||
});
|
||||
|
||||
it('prepends a carry-forward warning to owner pending prompts', () => {
|
||||
const prompt = buildOwnerPendingPrompt({
|
||||
chatJid: 'group@test',
|
||||
@@ -98,6 +151,75 @@ describe('message-runtime-prompts carry-forward guidance', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps owner pending prompts output-only when reviewer feedback exists', () => {
|
||||
const prompt = buildOwnerPendingPrompt({
|
||||
chatJid: 'group@test',
|
||||
timezone: 'UTC',
|
||||
turnOutputs: [
|
||||
makeTurnOutput('TASK_DONE\n현재 owner 결과', 'owner', {
|
||||
turn_number: 1,
|
||||
}),
|
||||
makeTurnOutput('DONE_WITH_CONCERNS\n현재 reviewer 피드백', 'reviewer', {
|
||||
id: 2,
|
||||
turn_number: 2,
|
||||
}),
|
||||
],
|
||||
recentHumanMessages: [makeHumanMessage('이전 작업의 사용자 메시지')],
|
||||
lastHumanMessage: '이전 작업의 사용자 메시지',
|
||||
});
|
||||
|
||||
expect(prompt).toContain('현재 owner 결과');
|
||||
expect(prompt).toContain('현재 reviewer 피드백');
|
||||
expect(prompt).not.toContain('이전 작업의 사용자 메시지');
|
||||
});
|
||||
|
||||
it('keeps arbiter prompts output-only when turn outputs exist', () => {
|
||||
const prompt = buildArbiterPromptForTask({
|
||||
task: makeTask({ status: 'arbiter_requested' }),
|
||||
chatJid: 'group@test',
|
||||
timezone: 'UTC',
|
||||
turnOutputs: [
|
||||
makeTurnOutput('TASK_DONE\nowner 주장', 'owner', {
|
||||
turn_number: 1,
|
||||
}),
|
||||
makeTurnOutput('DONE_WITH_CONCERNS\nreviewer 반박', 'reviewer', {
|
||||
id: 2,
|
||||
turn_number: 2,
|
||||
}),
|
||||
],
|
||||
recentMessages: [makeHumanMessage('예전 유저 지시')],
|
||||
labeledRecentMessages: [makeHumanMessage('예전 유저 지시')],
|
||||
});
|
||||
|
||||
expect(prompt).toContain('owner 주장');
|
||||
expect(prompt).toContain('reviewer 반박');
|
||||
expect(prompt).not.toContain('예전 유저 지시');
|
||||
});
|
||||
|
||||
it('preserves turn output order instead of timestamp interleaving', () => {
|
||||
const prompt = buildReviewerPendingPrompt({
|
||||
chatJid: 'group@test',
|
||||
timezone: 'UTC',
|
||||
turnOutputs: [
|
||||
makeTurnOutput('owner output first', 'owner', {
|
||||
turn_number: 1,
|
||||
created_at: '2026-04-20T02:00:00.000Z',
|
||||
}),
|
||||
makeTurnOutput('reviewer output second', 'reviewer', {
|
||||
id: 2,
|
||||
turn_number: 2,
|
||||
created_at: '2026-04-20T01:00:00.000Z',
|
||||
}),
|
||||
],
|
||||
recentHumanMessages: [],
|
||||
lastHumanMessage: null,
|
||||
});
|
||||
|
||||
expect(prompt.indexOf('owner output first')).toBeLessThan(
|
||||
prompt.indexOf('reviewer output second'),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not prepend the warning when there is no carried-forward turn output', () => {
|
||||
const prompt = buildPairedTurnPrompt({
|
||||
taskId: 'task-1',
|
||||
|
||||
@@ -79,6 +79,17 @@ export function buildPairedTurnPrompt(args: {
|
||||
);
|
||||
}
|
||||
|
||||
function turnOutputsOnlyPrompt(
|
||||
chatJid: string,
|
||||
timezone: string,
|
||||
turnOutputs: PairedTurnOutput[],
|
||||
): string {
|
||||
return prependCarriedForwardGuidance(
|
||||
formatMessages(turnOutputsToMessages(turnOutputs, chatJid), timezone),
|
||||
turnOutputs,
|
||||
);
|
||||
}
|
||||
|
||||
export function buildReviewerPendingPrompt(args: {
|
||||
chatJid: string;
|
||||
timezone: string;
|
||||
@@ -87,17 +98,7 @@ export function buildReviewerPendingPrompt(args: {
|
||||
lastHumanMessage: string | null | undefined;
|
||||
}): string {
|
||||
if (args.turnOutputs.length > 0) {
|
||||
return prependCarriedForwardGuidance(
|
||||
formatMessages(
|
||||
mergeHumanAndTurnOutputMessages(
|
||||
args.chatJid,
|
||||
args.recentHumanMessages,
|
||||
args.turnOutputs,
|
||||
),
|
||||
args.timezone,
|
||||
),
|
||||
args.turnOutputs,
|
||||
);
|
||||
return turnOutputsOnlyPrompt(args.chatJid, args.timezone, args.turnOutputs);
|
||||
}
|
||||
|
||||
if (!args.lastHumanMessage) {
|
||||
@@ -115,17 +116,7 @@ export function buildOwnerPendingPrompt(args: {
|
||||
lastHumanMessage: string | null | undefined;
|
||||
}): string {
|
||||
if (args.turnOutputs.length > 0) {
|
||||
return prependCarriedForwardGuidance(
|
||||
formatMessages(
|
||||
mergeHumanAndTurnOutputMessages(
|
||||
args.chatJid,
|
||||
args.recentHumanMessages,
|
||||
args.turnOutputs,
|
||||
),
|
||||
args.timezone,
|
||||
),
|
||||
args.turnOutputs,
|
||||
);
|
||||
return turnOutputsOnlyPrompt(args.chatJid, args.timezone, args.turnOutputs);
|
||||
}
|
||||
|
||||
if (!args.lastHumanMessage) {
|
||||
@@ -145,11 +136,7 @@ export function buildArbiterPromptForTask(args: {
|
||||
}): string {
|
||||
const messages =
|
||||
args.turnOutputs.length > 0
|
||||
? mergeHumanAndTurnOutputMessages(
|
||||
args.chatJid,
|
||||
args.recentMessages.filter((message) => !message.is_bot_message),
|
||||
args.turnOutputs,
|
||||
)
|
||||
? turnOutputsToMessages(args.turnOutputs, args.chatJid)
|
||||
: args.labeledRecentMessages;
|
||||
|
||||
return buildArbiterContextPrompt({
|
||||
|
||||
Reference in New Issue
Block a user