1.5.1: refined office, chat in the app language, current OpenAI models, own connector keys
Build Release Installers / Release for the tag (push) Successful in 16s
Build Release Installers / Windows installer (NSIS x64) (push) Successful in 1m48s
Build Release Installers / Linux packages (AppImage + deb x64) (push) Successful in 2m21s
CI / Typecheck, lint, tests, e2e (push) Successful in 3m52s
Build Release Installers / macOS installers (dmg + zip, x64 + arm64) (push) Successful in 7m15s
Build Release Installers / Release for the tag (push) Successful in 16s
Build Release Installers / Windows installer (NSIS x64) (push) Successful in 1m48s
Build Release Installers / Linux packages (AppImage + deb x64) (push) Successful in 2m21s
CI / Typecheck, lint, tests, e2e (push) Successful in 3m52s
Build Release Installers / macOS installers (dmg + zip, x64 + arm64) (push) Successful in 7m15s
- Home: the goal title box is two line boxes tall (2.8em), so a third line no longer peeks out. - Chat messages, reply fallbacks and assignment DMs follow the app display language; tasks and documents keep the working language. - OpenAI and GPT-account model lists match OpenAI's current line-up; default gpt-6.1-sol. - Onboarding connectors step takes the operator's Composio and logo.dev keys, then lists the toolkits the Composio project enables; without a key Connectors leaves the sidebar. - Google Antigravity removed end to end (migration 111), with the remote-revocation path only it used. - Office: 2x pixel people with outlines, rim light, walk, typing and blink; dithered real-clock lighting; detailed furniture and floors. - Per-agent model picker lists the provider's whole model list, live when connected. - Chat wake context breaks same-millisecond ties by insertion order; the mid-run inbox dedupes by message id. - The stored "clock" theme reads back instead of falling to "system". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
48dfddffef
commit
245ff66336
79 files changed
+1301
-1035
No files matched your search
@@ -43,7 +43,7 @@ test("the pinned CEO conversation is the Messages front door", async () => {
|
||||
await page.getByLabel("Acceptance criteria").fill("A working product, not documents about one.");
|
||||
await page.getByRole("button", { name: "Launch" }).click();
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
|
||||
// The CEO conversation is the first row, badged, and already selected —
|
||||
// opening Messages IS opening the CEO chat.
|
||||
|
||||
+23
-12
@@ -56,7 +56,9 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
|
||||
// The company exists after Launch; the wizard closes with the optional
|
||||
// connectors step.
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await expect(page.getByText("logo.dev publishable key")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
await expect(page.getByRole("button", { name: "Connectors", exact: true })).toHaveCount(0);
|
||||
|
||||
await expect(page.getByRole("button", { name: /Acme Operations/ })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Work", exact: true }).click();
|
||||
@@ -74,6 +76,12 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
|
||||
await page.getByLabel("Role").fill("CTO");
|
||||
await page.getByLabel("Department").fill("Engineering");
|
||||
await page.getByLabel("Monthly budget").fill("250");
|
||||
// The model picker offers the provider's whole list, whatever is picked now.
|
||||
const model = page.locator(".modal select[name=modelId]");
|
||||
await model.selectOption("gpt-6.1-sol");
|
||||
const offered = await model.locator("option").evaluateAll((options) => options.map((option) => (option as HTMLOptionElement).value));
|
||||
expect(offered).toEqual(expect.arrayContaining(["", "gpt-6.1-sol", "gpt-6-astra", "gpt-5.4-mini"]));
|
||||
await model.selectOption("");
|
||||
await page.locator(".modal").getByRole("button", { name: "Hire" }).click();
|
||||
await expect(page.getByText("Alex Chen").first()).toBeVisible();
|
||||
const hiredAgent = await page.evaluate(async () => {
|
||||
@@ -108,20 +116,19 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
|
||||
await page.getByRole("button", { name: "Edit Alex Chen" }).click();
|
||||
await page.locator(".modal").getByLabel("Name", { exact: true }).fill("Alex Turing");
|
||||
await page.getByLabel("Avatar emoji").fill("🚀");
|
||||
// The blank model field names the default of whichever provider is picked now.
|
||||
const editModel = page.locator(".modal").getByLabel("Model", { exact: true });
|
||||
await expect(editModel).toHaveAttribute("placeholder", "gpt-5.4");
|
||||
// The blank model choice names the default of whichever provider is picked now.
|
||||
const editModel = page.locator(".modal [name=modelId]");
|
||||
await expect(editModel.locator("option").first()).toHaveText("gpt-5.4 (provider default)");
|
||||
const editProvider = page.locator(".modal select[name=modelProviderId]");
|
||||
const savedProvider = await editProvider.inputValue();
|
||||
// Suggestions come from the picked provider only, and switching provider clears the model.
|
||||
await expect(page.locator("#edit-model-suggestions option").first()).toHaveAttribute("value", /.+/);
|
||||
await expect(page.locator("#edit-model-suggestions option[value^=claude]")).toHaveCount(0);
|
||||
await editModel.fill("gpt-5.4");
|
||||
// Choices come from the picked provider only, and switching provider clears the model.
|
||||
await expect(editModel.locator("option[value^=claude]")).toHaveCount(0);
|
||||
await editModel.selectOption("gpt-5.4");
|
||||
await editProvider.selectOption("");
|
||||
await expect(editModel).toHaveAttribute("placeholder", "");
|
||||
await expect(editModel).toHaveValue("");
|
||||
await expect(editModel).toBeDisabled();
|
||||
await editProvider.selectOption(savedProvider);
|
||||
await expect(editModel).toHaveValue("");
|
||||
await page.locator(".modal").getByRole("button", { name: "Save" }).click();
|
||||
await expect(page.getByText("Alex Turing").first()).toBeVisible();
|
||||
await expect(page.locator(".table-row", { hasText: "Alex Turing" }).locator(".avatar", { hasText: "🚀" })).toBeVisible();
|
||||
@@ -213,12 +220,15 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
|
||||
await page.getByRole("button", { name: "Approvals" }).click();
|
||||
await expect(page.getByText("Escalate release").first()).toBeVisible();
|
||||
|
||||
// The Connectors page shell renders whether or not the Composio catalog
|
||||
// is reachable from the test machine.
|
||||
// A saved Composio key brings Connectors into the sidebar; the page renders whether or not Composio answers.
|
||||
await page.getByRole("button", { name: "Settings" }).click();
|
||||
await page.getByRole("button", { name: "Integrations", exact: true }).click();
|
||||
await page.locator("form").filter({ hasText: "Composio API key" }).getByLabel("Key").fill("ak_e2e");
|
||||
await page.locator("form").filter({ hasText: "Composio API key" }).getByRole("button", { name: "Save" }).click();
|
||||
await page.getByRole("button", { name: "Connectors", exact: true }).click();
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Refresh" })).toHaveCount(0);
|
||||
// Without a Composio key the account check fails, and the page says the statuses may be stale.
|
||||
// A key Composio rejects fails the account check, and the page says the statuses may be stale.
|
||||
await expect(page.getByRole("alert").filter({ hasText: "Account status could not be checked with Composio" })).toBeVisible();
|
||||
// A connector failure is shown whole and can be copied.
|
||||
const catalogError = page.getByRole("alert").filter({ has: page.getByRole("button", { name: "Retry" }) }).filter({ hasNotText: "Account status" }).first();
|
||||
@@ -229,6 +239,7 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
|
||||
expect((await app.evaluate(({ clipboard }) => clipboard.readText())).trim()).toBe(shownReason);
|
||||
|
||||
await page.getByRole("button", { name: "Settings" }).click();
|
||||
await page.getByRole("button", { name: "General", exact: true }).click();
|
||||
await expect(page.getByText("AI CEO", { exact: true })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Providers" }).click();
|
||||
for (const label of ["Kimi", "Kimi CN", "Kimi Code"]) {
|
||||
|
||||
@@ -32,7 +32,7 @@ test("the library manages documents with versions and knowledge with history", a
|
||||
await page.getByLabel("Goal").fill("Keep company knowledge.");
|
||||
await page.getByLabel("Acceptance criteria").fill("Documents and knowledge are manageable.");
|
||||
await page.getByRole("button", { name: "Launch" }).click();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
await expect(page.getByRole("button", { name: /Stack Works/ })).toBeVisible();
|
||||
|
||||
const orgId = await page.evaluate(async () => {
|
||||
|
||||
@@ -43,7 +43,7 @@ test("the office floor opens agent details on click", async () => {
|
||||
await page.getByLabel("Acceptance criteria").fill("Clicking an agent opens their card.");
|
||||
await page.getByRole("button", { name: "Launch" }).click();
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
await expect(page.getByRole("button", { name: /Pixel Works/ })).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Office", exact: true }).click();
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function onboard(page: Page, company: string): Promise<void> {
|
||||
await page.getByLabel("Goal").fill("Ship.");
|
||||
await page.getByLabel("Acceptance criteria").fill("It ships.");
|
||||
await page.getByRole("button", { name: "Launch" }).click();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
await expect(page.getByRole("button", { name: "Settings" })).toBeVisible();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ test("onboarding resumes where it was left after the app is closed", async () =>
|
||||
app = await launch();
|
||||
page = await app.firstWindow();
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
// The step says when connector statuses could not be checked, as the Connectors page does.
|
||||
await expect(page.getByRole("alert").filter({ hasText: "Account status could not be checked with Composio" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
// Without a Composio key the step asks for one; skipping keeps Connectors out of the app.
|
||||
await expect(page.getByText("Composio API key")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
await expect(page.getByRole("button", { name: "Settings" })).toBeVisible();
|
||||
|
||||
// A cancelled second company leaves nothing behind for the next attempt.
|
||||
|
||||
@@ -20,6 +20,7 @@ test("every page and onboarding step draws its text on the pixel font's grid", a
|
||||
await expect(page.getByRole("heading", { name: "Welcome" })).toBeVisible();
|
||||
expect(await offGridText(page)).toEqual([]);
|
||||
await onboard(page, "Grid Co");
|
||||
await page.evaluate(() => window.agentCompany.setIntegrationKey({ name: "composio", value: "ak_e2e" }));
|
||||
for (const route of ["Home", "Work", "Team", "Office", "Messages", "Runs", "Files", "Library", "Approvals", "Connectors", "Settings"]) {
|
||||
await page.getByRole("button", { name: route, exact: true }).click();
|
||||
await page.waitForTimeout(400);
|
||||
|
||||
@@ -46,7 +46,7 @@ test("captures every page for visual review", async () => {
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Connectors" })).toBeVisible();
|
||||
await page.screenshot({ path: join(outDir, "05-onboarding-connectors.png"), fullPage: false });
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
await page.getByRole("button", { name: "Skip" }).click();
|
||||
|
||||
await expect(page.getByRole("button", { name: /OrbitOps/ })).toBeVisible();
|
||||
await snap(page, outDir, "10-home");
|
||||
@@ -56,6 +56,7 @@ test("captures every page for visual review", async () => {
|
||||
await go(page, "Messages", outDir, "13-messages");
|
||||
await go(page, "Files", outDir, "14-files");
|
||||
await go(page, "Approvals", outDir, "15-approvals");
|
||||
await page.evaluate(() => window.agentCompany.setIntegrationKey({ name: "composio", value: "ak_e2e" }));
|
||||
await go(page, "Connectors", outDir, "16-connectors");
|
||||
await go(page, "Settings", outDir, "17-settings");
|
||||
await go(page, "Office", outDir, "18-office");
|
||||
|
||||
Reference in new issue
Block a user