diff --git a/AgentCompany-New/tests/e2e/desktop.spec.ts b/AgentCompany-New/tests/e2e/desktop.spec.ts index e93dab24..f312b29e 100644 --- a/AgentCompany-New/tests/e2e/desktop.spec.ts +++ b/AgentCompany-New/tests/e2e/desktop.spec.ts @@ -164,6 +164,25 @@ test("onboards, hires, and creates work in the Electron shell", async () => { await page.getByRole("button", { name: "Set", exact: true }).click(); await expect(page.getByRole("button", { name: "active", exact: true })).toBeDisabled(); + // Virtual office sidebar toggle + await page.getByRole("button", { name: "Office" }).click(); + await expect(page.locator(".office-sidebar")).toBeVisible(); + await page.locator(".office-sidebar").getByRole("button", { name: "Close office" }).click(); + await expect(page.locator(".office-sidebar")).not.toBeVisible(); + + // Composio settings panel + await page.getByRole("button", { name: "Settings" }).click(); + await page.getByRole("button", { name: "Tools", exact: true }).click(); + await expect(page.getByText("External Tools")).toBeVisible(); + await expect(page.getByText("GitHub")).toBeVisible(); + + // @mention with canonical handle in chat + await page.getByRole("button", { name: "Messages" }).click(); + await page.getByText("general").first().click(); + await page.getByRole("textbox", { name: "Message" }).fill("@alexchen what is the status?"); + await page.getByRole("button", { name: "Send" }).click(); + await expect(page.getByText("Alex Chen").first()).toBeVisible(); + await verifyPagesDoNotOverflow(page); } finally { await app.close(); @@ -205,7 +224,7 @@ async function verifyPagesDoNotOverflow(page: Page): Promise { await page.getByRole("button", { name: nav, exact: true }).click(); await expectPageToFit(page); } - for (const tab of ["Workspace", "Billing", "Providers", "Skills", "Migration"]) { + for (const tab of ["Workspace", "Billing", "Providers", "Tools", "Skills", "Migration"]) { await page.getByRole("button", { name: "Settings" }).click(); await page.getByRole("button", { name: tab, exact: true }).click(); await expectPageToFit(page); diff --git a/AgentCompany-New/tests/integration/thread-participant-wake.test.ts b/AgentCompany-New/tests/integration/thread-participant-wake.test.ts new file mode 100644 index 00000000..e0f0bfa1 --- /dev/null +++ b/AgentCompany-New/tests/integration/thread-participant-wake.test.ts @@ -0,0 +1,63 @@ +import { randomUUID } from "node:crypto"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { describe, expect, test } from "vitest"; +import { DatabaseConnection } from "../../src/main/database/connection"; +import { OrganizationService } from "../../src/main/services/organization-service"; +import { ChatService } from "../../src/main/services/chat-service"; +import { dispatchAgentTool, type AgentToolContext } from "../../src/main/services/agent-tools"; + +function call(name: string, args: Record) { + return { id: `call_${randomUUID()}`, name, arguments: args }; +} + +describe("thread participant wake", () => { + test("human reply in a thread wakes agent participants without explicit @mention", () => { + const connection = new DatabaseConnection(join(tmpdir(), `ac-threadwake-${randomUUID()}.db`)); + const organization = new OrganizationService(connection.db); + let orgId = ""; + let ceoId = ""; + let employeeId = ""; + let providerId = ""; + connection.transaction(() => { + orgId = organization.createOrganization({ organizationName: "WakeCorp", employeeName: "Yang", mission: "m", goal: "g" }); + providerId = (connection.db.prepare("select id from providers where org_id = ? and provider_key = 'openai'").get(orgId) as { id: string }).id; + connection.db.prepare("update providers set enabled = 1 where id = ?").run(providerId); + ceoId = organization.createAgent({ + orgId, name: "CEO", role: "CEO", department: "Executive", + modelProviderId: providerId, modelId: "gpt-5.4", budgetCents: 25000, managerAgentId: null, + }); + employeeId = (connection.db.prepare("select id from employees where org_id = ?").get(orgId) as { id: string }).id; + }); + + const chat = new ChatService(connection.db); + const deps = { db: connection.db, organization, chat }; + const ctx: AgentToolContext = { + orgId, agent: { id: ceoId, modelProviderId: providerId, modelId: "gpt-5.4" }, + taskProjectId: null, taskGoalId: null, workspace: null, + }; + + const firstResult = dispatchAgentTool(deps, ctx, call("send_message", { channel: "general", body: "The plan is ready for review." })); + const threadId = /thread_id="([^"]+)"/.exec(firstResult)?.[1]; + expect(threadId).toBeTruthy(); + + const runsBeforeReply = connection.db.prepare("select count(*) as c from task_runs where org_id = ? and trigger = 'chat_mention'") + .get(orgId) as { c: number }; + + connection.transaction(() => { + chat.sendMessage({ + orgId, + spaceId: (connection.db.prepare("select id from chat_spaces where org_id = ? and name = 'general'").get(orgId) as { id: string }).id, + threadId: threadId!, + senderType: "employee", + senderId: employeeId, + bodyMd: "Looks good, please proceed with the deployment.", + attachmentFileIds: [], + }); + }); + + const runsAfterReply = connection.db.prepare("select count(*) as c from task_runs where org_id = ? and trigger = 'chat_mention'") + .get(orgId) as { c: number }; + expect(runsAfterReply.c).toBeGreaterThan(runsBeforeReply.c); + }); +});