Fix structured IPC envelope leakage
Normalize EJClaw structured send_message envelopes, preserve attachments, unwrap stored room payloads, and add regression tests.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { normalizeEjclawStructuredOutput } from './agent-protocol.js';
|
||||
import { isWatchCiTask } from './task-watch-status.js';
|
||||
import type { PairedTurnAttemptRecord, PairedTurnRecord } from './db.js';
|
||||
import type { StatusSnapshot, UsageRowSnapshot } from './status-dashboard.js';
|
||||
@@ -182,6 +183,37 @@ function buildRoomPreview(value: string, maxLength: number): string {
|
||||
return truncateText(redactSensitiveText(value).trim(), maxLength);
|
||||
}
|
||||
|
||||
function decodeCommonHtmlEntities(value: string): string {
|
||||
return value
|
||||
.replace(/"|"|"/gi, '"')
|
||||
.replace(/'|'|'/gi, "'")
|
||||
.replace(/<|<|</gi, '<')
|
||||
.replace(/>|>|>/gi, '>')
|
||||
.replace(/&|&|&/gi, '&');
|
||||
}
|
||||
|
||||
function hasStructuredOutputHint(value: string): boolean {
|
||||
return value.includes('"ejclaw"') || /"ejclaw"/i.test(value);
|
||||
}
|
||||
|
||||
function normalizeRoomMessageContent(value: string): string | null {
|
||||
if (!hasStructuredOutputHint(value)) return value;
|
||||
|
||||
const candidates = [value, decodeCommonHtmlEntities(value)];
|
||||
for (const candidate of candidates) {
|
||||
const normalized = normalizeEjclawStructuredOutput(candidate);
|
||||
if (normalized.output?.visibility === 'silent') return null;
|
||||
if (
|
||||
normalized.output?.visibility === 'public' &&
|
||||
normalized.output.text !== candidate
|
||||
) {
|
||||
return normalized.output.text;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function stableHash(value: string): string {
|
||||
let hash = 0;
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
@@ -405,15 +437,17 @@ export function sanitizeScheduledTask(
|
||||
};
|
||||
}
|
||||
|
||||
function sanitizeRoomMessage(message: NewMessage): WebDashboardRoomMessage {
|
||||
function sanitizeRoomMessage(
|
||||
message: NewMessage,
|
||||
): WebDashboardRoomMessage | null {
|
||||
const content = normalizeRoomMessageContent(message.content ?? '');
|
||||
if (content === null) return null;
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
sender: message.sender,
|
||||
senderName: message.sender_name || message.sender,
|
||||
content: buildRoomPreview(
|
||||
message.content ?? '',
|
||||
ROOM_MESSAGE_PREVIEW_MAX_LENGTH,
|
||||
),
|
||||
content: buildRoomPreview(content, ROOM_MESSAGE_PREVIEW_MAX_LENGTH),
|
||||
timestamp: message.timestamp,
|
||||
isFromMe: !!message.is_from_me,
|
||||
isBotMessage: !!message.is_bot_message,
|
||||
@@ -568,7 +602,10 @@ export function buildWebDashboardRoomActivity(args: {
|
||||
pendingTasks: args.entry.pendingTasks,
|
||||
messages: args.messages
|
||||
.filter((message) => !isTaskStatusMessage(message))
|
||||
.map(sanitizeRoomMessage),
|
||||
.map(sanitizeRoomMessage)
|
||||
.filter((message): message is WebDashboardRoomMessage =>
|
||||
Boolean(message),
|
||||
),
|
||||
pairedTask: args.pairedTask
|
||||
? {
|
||||
id: args.pairedTask.id,
|
||||
|
||||
Reference in New Issue
Block a user