fix: require discord evidence for legacy dashboard outbound

This commit is contained in:
ejclaw
2026-04-29 01:52:13 +09:00
parent 0e77864f39
commit d7649f006f
2 changed files with 110 additions and 6 deletions

View File

@@ -442,6 +442,84 @@ describe('web dashboard room activity data', () => {
]);
});
it('hides legacy delivered work items without discord delivery evidence', () => {
const activity = buildWebDashboardRoomActivity({
serviceId: 'codex-main',
entry: {
jid: 'dc:ops',
name: '#ops',
folder: 'ops',
agentType: 'codex',
status: 'inactive',
elapsedMs: null,
pendingMessages: false,
pendingTasks: 0,
},
pairedTask: null,
turns: [],
attempts: [],
outputs: [],
outboundItems: [
makeDeliveredWorkItem({
id: 103,
delivery_role: null,
delivery_message_id: null,
result_payload: '서비스 재시작으로 이전 작업이 중단됐습니다.',
}),
],
messages: [],
});
expect(activity.messages).toEqual([]);
});
it('keeps legacy delivered work items when a discord echo exists', () => {
const activity = buildWebDashboardRoomActivity({
serviceId: 'codex-main',
entry: {
jid: 'dc:ops',
name: '#ops',
folder: 'ops',
agentType: 'codex',
status: 'inactive',
elapsedMs: null,
pendingMessages: false,
pendingTasks: 0,
},
pairedTask: null,
turns: [],
attempts: [],
outputs: [],
outboundItems: [
makeDeliveredWorkItem({
id: 104,
delivery_message_id: null,
result_payload: 'TASK_DONE\n\nlegacy delivered output',
}),
],
messages: [
{
id: 'discord-legacy-echo',
chat_jid: 'dc:ops',
sender: 'bot-owner',
sender_name: '오너',
content: 'TASK_DONE\n\nlegacy delivered output',
timestamp: '2026-04-26T05:30:11.000Z',
is_from_me: true,
is_bot_message: true,
message_source_kind: 'bot',
},
],
});
expect(activity.messages).toEqual([
expect.objectContaining({
id: 'work:104',
content: 'TASK_DONE\n\nlegacy delivered output',
}),
]);
});
it('merges canonical outbound and recent messages in chronological order', () => {
const activity = buildWebDashboardRoomActivity({
serviceId: 'codex-main',

View File

@@ -575,6 +575,15 @@ function isDuplicateOfCanonicalOutbound(
});
}
function hasDiscordEchoForCanonicalOutbound(
canonical: WebDashboardRoomMessage,
messages: WebDashboardRoomMessage[],
): boolean {
return messages.some((message) =>
isDuplicateOfCanonicalOutbound(message, [canonical]),
);
}
function compareRoomMessagesByTimestamp(
a: WebDashboardRoomMessage,
b: WebDashboardRoomMessage,
@@ -670,19 +679,36 @@ export function buildWebDashboardRoomActivity(args: {
? (latestAttemptByTurnId.get(currentTurn.turn_id) ?? null)
: null;
const outputLimit = args.outputLimit ?? 4;
const canonicalOutboundMessages = (args.outboundItems ?? [])
.map(sanitizeCanonicalOutboundMessage)
const sanitizedRecentMessages = args.messages
.filter((message) => !isTaskStatusMessage(message))
.map(sanitizeRoomMessage)
.filter((message): message is WebDashboardRoomMessage => Boolean(message));
const canonicalOutboundMessages = (args.outboundItems ?? [])
.map((item) => ({
item,
message: sanitizeCanonicalOutboundMessage(item),
}))
.filter(
(
candidate,
): candidate is {
item: WorkItem;
message: WebDashboardRoomMessage;
} => Boolean(candidate.message),
)
.filter(
({ item, message }) =>
Boolean(item.delivery_message_id) ||
hasDiscordEchoForCanonicalOutbound(message, sanitizedRecentMessages),
)
.map(({ message }) => message);
const canonicalDeliveryMessageIds = new Set(
(args.outboundItems ?? [])
.map((item) => item.delivery_message_id)
.filter((id): id is string => Boolean(id)),
);
const recentMessages = args.messages
const recentMessages = sanitizedRecentMessages
.filter((message) => !canonicalDeliveryMessageIds.has(message.id))
.filter((message) => !isTaskStatusMessage(message))
.map(sanitizeRoomMessage)
.filter((message): message is WebDashboardRoomMessage => Boolean(message))
.filter(
(message) =>
!isDuplicateOfCanonicalOutbound(message, canonicalOutboundMessages),