Q147: the conversation header lists its real members, how each agent replies, and who can read
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
02309546dc
commit
983d37937d
13 files changed
+173
-44
No files matched your search
@@ -204,6 +204,17 @@ test("the pinned CEO conversation is the Messages front door", async () => {
|
||||
}
|
||||
await expect(start).toBeVisible();
|
||||
await expect(chatLog.getByText("General line 0", { exact: true })).toBeVisible();
|
||||
|
||||
// The header lists the real members, how each agent replies, and who can read.
|
||||
await page.getByRole("button", { name: /^Members \(/ }).click();
|
||||
const roster = page.getByRole("menu");
|
||||
await expect(roster).toContainText("Everyone in the company can read this channel.");
|
||||
await expect(roster.locator(".members-roster")).toHaveCSS("overflow-y", "auto");
|
||||
await expect(roster).toContainText("Replies when @mentioned, in threads it joined, and to messages that name no one");
|
||||
await page.keyboard.press("Escape");
|
||||
await page.locator(".convo-item").first().click();
|
||||
await page.getByRole("button", { name: /^Members \(/ }).click();
|
||||
await expect(page.getByRole("menu")).toContainText("Replies to every message");
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ describe("a message's reply status", () => {
|
||||
const status = () => new SnapshotService(connection.db).load().chatReplyStatuses.filter((item) => item.messageId === messageId);
|
||||
|
||||
expect(status()).toEqual([{ messageId, agentId: ceoId, state: "queued", detail: null, resumesAt: null }]);
|
||||
// The CEO answers channel messages that name no one.
|
||||
const general = (connection.db.prepare("select id from chat_spaces where org_id = ? and name = 'general'").get(orgId) as { id: string }).id;
|
||||
expect(new SnapshotService(connection.db).load().chatSpaceAnchors.find((item) => item.spaceId === general)).toEqual({ spaceId: general, agentId: ceoId });
|
||||
// A second message folded into the same queued reply gets the same status.
|
||||
const follow = connection.transaction(() => new ChatService(connection.db).sendMessage({
|
||||
orgId, spaceId: dm, threadId: (connection.db.prepare("select thread_id from chat_messages where id = ?").get(messageId) as { thread_id: string }).thread_id,
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { spaceMembers, spaceReaders } from "../../src/renderer/src/pages/chat/chat-helpers";
|
||||
import type { AgentRecord, ChatMembershipRecord, ChatSpaceRecord, EmployeeRecord } from "../../src/shared/domain";
|
||||
|
||||
const ORG = "org_1";
|
||||
|
||||
function agent(id: string, name: string): AgentRecord {
|
||||
return {
|
||||
id, orgId: ORG, name, role: name, department: "Engineering", modelProviderId: null, modelId: null,
|
||||
budgetCents: 0, managerAgentId: null, title: name, roleLevel: "staff",
|
||||
modelStrategy: "explicit", workspaceId: null, heartbeatEnabled: false, heartbeatIntervalSec: 0,
|
||||
lifecycle: "active", avatarEmoji: null, avatarColor: null, avatarImage: null, createdAt: "2026-01-01T00:00:00Z",
|
||||
};
|
||||
}
|
||||
|
||||
const employees: EmployeeRecord[] = [{ id: "emp_yang", orgId: ORG, displayName: "Yang", role: "founder", avatarEmoji: null, avatarColor: null, avatarImage: null, createdAt: "2026-01-01T00:00:00Z" }];
|
||||
const agents = [agent("agt_ceo", "CEO"), agent("agt_dana", "Dana"), agent("agt_eve", "Eve")];
|
||||
const member = (spaceId: string, principalType: "employee" | "agent", principalId: string): ChatMembershipRecord => ({ spaceId, principalType, principalId, role: "member", muteState: "unmuted" });
|
||||
const space = (id: string, spaceType: string, visibility: string): ChatSpaceRecord => ({ id, orgId: ORG, spaceType, name: id, topic: "t", visibility, retentionPolicy: "standard", createdAt: "2026-01-01T00:00:00Z" });
|
||||
|
||||
describe("who is in a conversation", () => {
|
||||
test("lists every member, spoken or not, with how each agent replies", () => {
|
||||
const memberships = [member("eng", "employee", "emp_yang"), member("eng", "agent", "agt_ceo"), member("eng", "agent", "agt_dana"), member("dm", "employee", "emp_yang"), member("dm", "agent", "agt_dana")];
|
||||
expect(spaceMembers(space("eng", "department", "workspace"), memberships, employees, agents, "agt_ceo")).toEqual([
|
||||
{ id: "emp_yang", name: "Yang", bot: false, duty: "Reads and writes" },
|
||||
{ id: "agt_ceo", name: "CEO", bot: true, duty: "Replies when @mentioned, in threads it joined, and to messages that name no one" },
|
||||
{ id: "agt_dana", name: "Dana", bot: true, duty: "Replies when @mentioned and in threads it joined" },
|
||||
]);
|
||||
expect(spaceMembers(space("dm", "direct", "restricted"), memberships, employees, agents, null).map((item) => item.duty)).toEqual(["Reads and writes", "Replies to every message"]);
|
||||
});
|
||||
|
||||
test("says who can read beyond the member list", () => {
|
||||
expect(spaceReaders(space("eng", "department", "workspace"))).toBe("Everyone in the company can read this channel.");
|
||||
expect(spaceReaders(space("ops", "project", "restricted"))).toBe("Only members can read this channel.");
|
||||
expect(spaceReaders(space("dm", "direct", "restricted"))).toBe("Only the people in this conversation can read it.");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user