feat(messenger): channels + @mentions in the CRM SDK adapter
CrmMessagingAdapter now implements the full SDK contract: - browseChannels/createChannel/leaveChannel over the be-crm channel door (crm.messenger.channel.*); joinChannel is a governed public self-join over the socket (openThread) — the BFF has no join verb, OPA enforces it - listMembers over crm.messenger.members → drives @mention autocomplete - send forwards mentions[] on the socket path (→ IIOS MENTION inbox items) So the CRM messenger (rendered by the SDK) now gets sectioned Channels/DMs, browse+join+create, and @mention autocomplete/highlight — no embedded code. (SDK re-packed to the local tarball; publish for deploy.) tsc + next build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Generated
+1
-1
@@ -1030,7 +1030,7 @@
|
||||
"node_modules/@insignia/iios-messaging-ui": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "file:../iios/packages/iios-messaging-ui/insignia-iios-messaging-ui-0.1.0.tgz",
|
||||
"integrity": "sha512-6pSVWACjex1sXWzVqx+42rM6vgWEJ+1ud5y3DLKogJiQmfgFZgCB8vTw0PN/5FECugXC7iGuc9rV9RsHsP+EDw==",
|
||||
"integrity": "sha512-B6Vzb75lBv4W3ZuKE4muMSz/OyUrE0NxImUI6sHaCBby5xveHVyanPiwkxiaqg9zZPYQMYiZRMONYwlNqyJvig==",
|
||||
"peerDependencies": {
|
||||
"@insignia/iios-kernel-client": "*",
|
||||
"react": ">=18"
|
||||
|
||||
@@ -6,11 +6,15 @@
|
||||
// When no socket is available (token failed / demo), it degrades to a 4s history poll.
|
||||
|
||||
import type {
|
||||
ChannelSummary,
|
||||
ChannelVisibility,
|
||||
Conversation,
|
||||
CreateChannelInput,
|
||||
Membership,
|
||||
Message,
|
||||
MessageEvent,
|
||||
MessagingAdapter,
|
||||
Person,
|
||||
Reaction,
|
||||
SendOpts,
|
||||
Unsubscribe,
|
||||
@@ -106,11 +110,11 @@ export class CrmMessagingAdapter implements MessagingAdapter {
|
||||
|
||||
async send(threadId: string, content: string, opts?: SendOpts): Promise<Message> {
|
||||
if (this.socket) {
|
||||
const m = await this.socket.sendMessage(
|
||||
threadId,
|
||||
content,
|
||||
opts?.parentInteractionId ? { parentInteractionId: opts.parentInteractionId } : undefined,
|
||||
);
|
||||
const sendOpts = {
|
||||
...(opts?.parentInteractionId ? { parentInteractionId: opts.parentInteractionId } : {}),
|
||||
...(opts?.mentions && opts.mentions.length ? { mentions: opts.mentions } : {}),
|
||||
};
|
||||
const m = await this.socket.sendMessage(threadId, content, Object.keys(sendOpts).length ? sendOpts : undefined);
|
||||
return this.fromKernel(m);
|
||||
}
|
||||
const m = await this.sdk.command<MessageDTO>("crm.messenger.send", { threadId, content });
|
||||
@@ -152,6 +156,46 @@ export class CrmMessagingAdapter implements MessagingAdapter {
|
||||
if (this.socket) await this.socket.markRead(threadId, messageId);
|
||||
}
|
||||
|
||||
// ── channels + members (BFF, except join which is a governed socket self-join) ──
|
||||
async browseChannels(): Promise<ChannelSummary[]> {
|
||||
const rows = await this.sdk.query<Array<{ threadId: string; name: string; topic: string | null; visibility: string; memberCount: number; joined: boolean }>>(
|
||||
"crm.messenger.channel.browse",
|
||||
{},
|
||||
);
|
||||
return rows.map((c) => ({
|
||||
threadId: c.threadId,
|
||||
name: c.name,
|
||||
topic: c.topic,
|
||||
visibility: (c.visibility === "private" ? "private" : "public") as ChannelVisibility,
|
||||
memberCount: c.memberCount,
|
||||
joined: c.joined,
|
||||
}));
|
||||
}
|
||||
|
||||
async createChannel(input: CreateChannelInput): Promise<{ threadId: string }> {
|
||||
return this.sdk.command<{ threadId: string }>("crm.messenger.channel.create", {
|
||||
name: input.name,
|
||||
...(input.topic ? { topic: input.topic } : {}),
|
||||
visibility: input.visibility,
|
||||
});
|
||||
}
|
||||
|
||||
async joinChannel(threadId: string): Promise<void> {
|
||||
// Governed public self-join over the socket (the BFF has no join verb; OPA enforces it).
|
||||
if (!this.socket) throw new Error("joining a channel needs a live connection");
|
||||
await this.socket.openThread(threadId);
|
||||
this.joined.add(threadId);
|
||||
}
|
||||
|
||||
async leaveChannel(threadId: string): Promise<void> {
|
||||
await this.sdk.command("crm.messenger.channel.leave", { threadId });
|
||||
}
|
||||
|
||||
async listMembers(threadId: string): Promise<Person[]> {
|
||||
const rows = await this.sdk.query<Array<{ userId: string; displayName: string; role: string }>>("crm.messenger.members", { threadId });
|
||||
return rows.map((r) => ({ id: r.userId, name: r.displayName, kind: "staff" as const }));
|
||||
}
|
||||
|
||||
// ── polling fallback (no socket) ───────────────────────────────
|
||||
private startPoll(threadId: string): void {
|
||||
if (this.polls.has(threadId)) return;
|
||||
|
||||
Reference in New Issue
Block a user