fix: keep paired review prompts output-only
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
buildArbiterPromptForTask,
|
||||||
buildFinalizePendingPrompt,
|
buildFinalizePendingPrompt,
|
||||||
buildOwnerPendingPrompt,
|
buildOwnerPendingPrompt,
|
||||||
buildPairedTurnPrompt,
|
buildPairedTurnPrompt,
|
||||||
buildReviewerPendingPrompt,
|
buildReviewerPendingPrompt,
|
||||||
} from './message-runtime-prompts.js';
|
} from './message-runtime-prompts.js';
|
||||||
import type { NewMessage, PairedTurnOutput } from './types.js';
|
import type { NewMessage, PairedTask, PairedTurnOutput } from './types.js';
|
||||||
|
|
||||||
const CARRY_FORWARD_MARKER =
|
const CARRY_FORWARD_MARKER =
|
||||||
'[Carried forward context from the previous task: latest owner final]';
|
'[Carried forward context from the previous task: latest owner final]';
|
||||||
@@ -27,6 +28,7 @@ function makeHumanMessage(content: string): NewMessage {
|
|||||||
function makeTurnOutput(
|
function makeTurnOutput(
|
||||||
outputText: string,
|
outputText: string,
|
||||||
role: PairedTurnOutput['role'] = 'owner',
|
role: PairedTurnOutput['role'] = 'owner',
|
||||||
|
overrides: Partial<PairedTurnOutput> = {},
|
||||||
): PairedTurnOutput {
|
): PairedTurnOutput {
|
||||||
return {
|
return {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -35,6 +37,37 @@ function makeTurnOutput(
|
|||||||
role,
|
role,
|
||||||
output_text: outputText,
|
output_text: outputText,
|
||||||
created_at: '2026-04-20T00:59:00.000Z',
|
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', () => {
|
it('prepends a carry-forward warning to owner pending prompts', () => {
|
||||||
const prompt = buildOwnerPendingPrompt({
|
const prompt = buildOwnerPendingPrompt({
|
||||||
chatJid: 'group@test',
|
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', () => {
|
it('does not prepend the warning when there is no carried-forward turn output', () => {
|
||||||
const prompt = buildPairedTurnPrompt({
|
const prompt = buildPairedTurnPrompt({
|
||||||
taskId: 'task-1',
|
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: {
|
export function buildReviewerPendingPrompt(args: {
|
||||||
chatJid: string;
|
chatJid: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
@@ -87,17 +98,7 @@ export function buildReviewerPendingPrompt(args: {
|
|||||||
lastHumanMessage: string | null | undefined;
|
lastHumanMessage: string | null | undefined;
|
||||||
}): string {
|
}): string {
|
||||||
if (args.turnOutputs.length > 0) {
|
if (args.turnOutputs.length > 0) {
|
||||||
return prependCarriedForwardGuidance(
|
return turnOutputsOnlyPrompt(args.chatJid, args.timezone, args.turnOutputs);
|
||||||
formatMessages(
|
|
||||||
mergeHumanAndTurnOutputMessages(
|
|
||||||
args.chatJid,
|
|
||||||
args.recentHumanMessages,
|
|
||||||
args.turnOutputs,
|
|
||||||
),
|
|
||||||
args.timezone,
|
|
||||||
),
|
|
||||||
args.turnOutputs,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.lastHumanMessage) {
|
if (!args.lastHumanMessage) {
|
||||||
@@ -115,17 +116,7 @@ export function buildOwnerPendingPrompt(args: {
|
|||||||
lastHumanMessage: string | null | undefined;
|
lastHumanMessage: string | null | undefined;
|
||||||
}): string {
|
}): string {
|
||||||
if (args.turnOutputs.length > 0) {
|
if (args.turnOutputs.length > 0) {
|
||||||
return prependCarriedForwardGuidance(
|
return turnOutputsOnlyPrompt(args.chatJid, args.timezone, args.turnOutputs);
|
||||||
formatMessages(
|
|
||||||
mergeHumanAndTurnOutputMessages(
|
|
||||||
args.chatJid,
|
|
||||||
args.recentHumanMessages,
|
|
||||||
args.turnOutputs,
|
|
||||||
),
|
|
||||||
args.timezone,
|
|
||||||
),
|
|
||||||
args.turnOutputs,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.lastHumanMessage) {
|
if (!args.lastHumanMessage) {
|
||||||
@@ -145,11 +136,7 @@ export function buildArbiterPromptForTask(args: {
|
|||||||
}): string {
|
}): string {
|
||||||
const messages =
|
const messages =
|
||||||
args.turnOutputs.length > 0
|
args.turnOutputs.length > 0
|
||||||
? mergeHumanAndTurnOutputMessages(
|
? turnOutputsToMessages(args.turnOutputs, args.chatJid)
|
||||||
args.chatJid,
|
|
||||||
args.recentMessages.filter((message) => !message.is_bot_message),
|
|
||||||
args.turnOutputs,
|
|
||||||
)
|
|
||||||
: args.labeledRecentMessages;
|
: args.labeledRecentMessages;
|
||||||
|
|
||||||
return buildArbiterContextPrompt({
|
return buildArbiterContextPrompt({
|
||||||
|
|||||||
Reference in New Issue
Block a user