feat(FN-3971): add aggregate mailbox view for all agents
Adds an "All Agents" unified mailbox view to the dashboard, including a new aggregate API endpoint in `@fusion/core`, updated MailboxView UI with selection controls, CSS styling, and comprehensive tests across core and dashboard packages, with documentation updates. Fusion-Task-Id: FN-3971
This commit is contained in:
@@ -703,6 +703,72 @@ describe("MessageStore", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllAgentToAgentMessages() / getUnreadAgentToAgentCount()", () => {
|
||||
it("returns newest-first agent-to-agent messages only", () => {
|
||||
store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: "agent-2",
|
||||
toType: "agent",
|
||||
content: "first",
|
||||
type: "agent-to-agent",
|
||||
});
|
||||
const second = store.sendMessage({
|
||||
fromId: "agent-2",
|
||||
fromType: "agent",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "second",
|
||||
type: "agent-to-agent",
|
||||
});
|
||||
store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: "dashboard",
|
||||
toType: "user",
|
||||
content: "not included",
|
||||
type: "agent-to-user",
|
||||
});
|
||||
|
||||
const messages = store.getAllAgentToAgentMessages();
|
||||
expect(messages).toHaveLength(2);
|
||||
expect(messages[0].id).toBe(second.id);
|
||||
expect(messages.every((message) => message.type === "agent-to-agent")).toBe(true);
|
||||
});
|
||||
|
||||
it("counts unread agent-to-agent messages only", () => {
|
||||
const unread = store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: "agent-2",
|
||||
toType: "agent",
|
||||
content: "unread",
|
||||
type: "agent-to-agent",
|
||||
});
|
||||
const read = store.sendMessage({
|
||||
fromId: "agent-2",
|
||||
fromType: "agent",
|
||||
toId: "agent-1",
|
||||
toType: "agent",
|
||||
content: "read",
|
||||
type: "agent-to-agent",
|
||||
});
|
||||
store.sendMessage({
|
||||
fromId: "agent-1",
|
||||
fromType: "agent",
|
||||
toId: "dashboard",
|
||||
toType: "user",
|
||||
content: "non-agent",
|
||||
type: "agent-to-user",
|
||||
});
|
||||
|
||||
store.markAsRead(read.id);
|
||||
|
||||
expect(store.getUnreadAgentToAgentCount()).toBe(1);
|
||||
expect(store.getAllAgentToAgentMessages().map((message) => message.id)).toContain(unread.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("events", () => {
|
||||
it("emits message:sent event on send", () => {
|
||||
const events: Message[] = [];
|
||||
|
||||
@@ -417,6 +417,32 @@ export class MessageStore extends EventEmitter<MessageStoreEvents> {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all agent-to-agent messages across all agents.
|
||||
* @returns Array of messages (newest first)
|
||||
*/
|
||||
getAllAgentToAgentMessages(): Message[] {
|
||||
const rows = this.db.prepare(`
|
||||
SELECT * FROM messages
|
||||
WHERE type = ?
|
||||
ORDER BY createdAt DESC, rowid DESC
|
||||
`).all("agent-to-agent");
|
||||
|
||||
return (rows as unknown as MessageRow[]).map((row) => this.rowToMessage(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unread count across all agent-to-agent messages.
|
||||
*/
|
||||
getUnreadAgentToAgentCount(): number {
|
||||
const row = this.db.prepare(`
|
||||
SELECT COUNT(*) as count FROM messages
|
||||
WHERE type = ? AND read = 0
|
||||
`).get("agent-to-agent") as { count: number } | undefined;
|
||||
|
||||
return row?.count ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or update the hook used when messages are sent to agents.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user