Files
openclaw/extensions/qa-lab/src/bus-queries.ts
2026-04-18 20:37:12 +01:00

163 lines
5.0 KiB
TypeScript

import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import type {
QaBusAttachment,
QaBusConversation,
QaBusEvent,
QaBusMessage,
QaBusPollInput,
QaBusPollResult,
QaBusReadMessageInput,
QaBusSearchMessagesInput,
QaBusStateSnapshot,
QaBusThread,
} from "./runtime-api.js";
export const DEFAULT_ACCOUNT_ID = "default";
export function normalizeAccountId(raw?: string): string {
const trimmed = raw?.trim();
return trimmed || DEFAULT_ACCOUNT_ID;
}
export function normalizeConversationFromTarget(target: string): {
conversation: QaBusConversation;
threadId?: string;
} {
const trimmed = target.trim();
if (trimmed.startsWith("thread:")) {
const rest = trimmed.slice("thread:".length);
const slash = rest.indexOf("/");
if (slash > 0) {
return {
conversation: { id: rest.slice(0, slash), kind: "channel" },
threadId: rest.slice(slash + 1),
};
}
}
if (trimmed.startsWith("channel:")) {
return {
conversation: { id: trimmed.slice("channel:".length), kind: "channel" },
};
}
if (trimmed.startsWith("dm:")) {
return {
conversation: { id: trimmed.slice("dm:".length), kind: "direct" },
};
}
return {
conversation: { id: trimmed, kind: "direct" },
};
}
export function cloneMessage(message: QaBusMessage): QaBusMessage {
return {
...message,
conversation: { ...message.conversation },
attachments: (message.attachments ?? []).map((attachment) => cloneAttachment(attachment)),
reactions: message.reactions.map((reaction) => ({ ...reaction })),
};
}
function cloneAttachment(attachment: QaBusAttachment): QaBusAttachment {
return { ...attachment };
}
export function cloneEvent(event: QaBusEvent): QaBusEvent {
switch (event.kind) {
case "inbound-message":
case "outbound-message":
case "message-edited":
case "message-deleted":
case "reaction-added":
return { ...event, message: cloneMessage(event.message) };
case "thread-created":
return { ...event, thread: { ...event.thread } };
}
throw new Error("Unsupported QA bus event kind");
}
export function buildQaBusSnapshot(params: {
cursor: number;
conversations: Map<string, QaBusConversation>;
threads: Map<string, QaBusThread>;
messages: Map<string, QaBusMessage>;
events: QaBusEvent[];
}): QaBusStateSnapshot {
return {
cursor: params.cursor,
conversations: Array.from(params.conversations.values()).map((conversation) =>
Object.assign({}, conversation),
),
threads: Array.from(params.threads.values()).map((thread) => Object.assign({}, thread)),
messages: Array.from(params.messages.values()).map((message) => cloneMessage(message)),
events: params.events.map((event) => cloneEvent(event)),
};
}
export function readQaBusMessage(params: {
messages: Map<string, QaBusMessage>;
input: QaBusReadMessageInput;
}) {
const message = params.messages.get(params.input.messageId);
if (!message) {
throw new Error(`qa-bus message not found: ${params.input.messageId}`);
}
return cloneMessage(message);
}
export function searchQaBusMessages(params: {
messages: Map<string, QaBusMessage>;
input: QaBusSearchMessagesInput;
}) {
const accountId = normalizeAccountId(params.input.accountId);
const limit = Math.max(1, Math.min(params.input.limit ?? 20, 100));
const query = normalizeOptionalLowercaseString(params.input.query);
return Array.from(params.messages.values())
.filter((message) => message.accountId === accountId)
.filter((message) =>
params.input.conversationId ? message.conversation.id === params.input.conversationId : true,
)
.filter((message) =>
params.input.threadId ? message.threadId === params.input.threadId : true,
)
.filter((message) => {
if (!query) {
return true;
}
const attachmentHaystack = message.attachments ?? [];
const searchableAttachmentText = attachmentHaystack
.flatMap((attachment) => [
attachment.fileName,
attachment.altText,
attachment.transcript,
attachment.mimeType,
])
.filter((value): value is string => Boolean(value))
.join(" ")
.toLowerCase();
const messageText = normalizeOptionalLowercaseString(message.text) ?? "";
return `${messageText} ${searchableAttachmentText}`.includes(query);
})
.slice(-limit)
.map((message) => cloneMessage(message));
}
export function pollQaBusEvents(params: {
events: QaBusEvent[];
cursor: number;
input?: QaBusPollInput;
}): QaBusPollResult {
const accountId = normalizeAccountId(params.input?.accountId);
const startCursor = params.input?.cursor ?? 0;
const effectiveStartCursor = params.cursor < startCursor ? 0 : startCursor;
const limit = Math.max(1, Math.min(params.input?.limit ?? 100, 500));
const matches = params.events
.filter((event) => event.accountId === accountId && event.cursor > effectiveStartCursor)
.slice(0, limit)
.map((event) => cloneEvent(event));
return {
cursor: params.cursor,
events: matches,
};
}