evOo-local/packages/core/src/message-visibility.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

53 lines
2.0 KiB
TypeScript

import type { MessageBlock } from "@rakazo/contracts";
type PresentableMessage = {
runId?: string;
blocks: readonly MessageBlock[];
};
export type UserVisibleMessagesOptions = {
/**
* Keep `bot_message_sent` / `bot_message_received` rows as compact chips
* (web CollaborationMarker; mobile AgentEventLabel). Peer bodies stay hidden.
*/
includePeerReceipts?: boolean;
/** Peer-run ids from `run.trigger === "bot_message"` when receipts may be out of window. */
knownPeerRunIds?: Iterable<string>;
/**
* Surface a peer-run's own `text` reply (e.g. a delegating bot's summary to the user)
* alongside `ask` cards. Defaults to true for chat-thread rendering; set false for
* contexts like sidebar previews that should stay ask-only and never echo peer chatter.
*/
includeDelegatedReplyText?: boolean;
};
export function isPeerReceiptBlocks(blocks: readonly MessageBlock[]): boolean {
return blocks.some(
(block) => block.kind === "bot_message_sent" || block.kind === "bot_message_received",
);
}
/** Drop peer-run activity/replies; optionally keep sent/received receipt rows. */
export function userVisibleMessages<T extends PresentableMessage>(
messages: readonly T[],
options: UserVisibleMessagesOptions = {},
): T[] {
const peerRunIds = new Set([
...(options.knownPeerRunIds ?? []),
...messages
.filter((message) => message.blocks.some((block) => block.kind === "bot_message_received"))
.flatMap((message) => (message.runId ? [message.runId] : [])),
]);
const includePeerReceipts = options.includePeerReceipts === true;
return messages.filter((message) => {
if (isPeerReceiptBlocks(message.blocks)) return includePeerReceipts;
if (!message.runId || !peerRunIds.has(message.runId)) return true;
// Keep peer-run ask cards, and (unless the caller opts out) the bot's own text reply.
const includeText = options.includeDelegatedReplyText !== false;
return message.blocks.some(
(block) => block.kind === "ask" || (includeText && block.kind === "text"),
);
});
}