Avatar photo uploads and a global handbrake for the agent engine

Two operator asks, one review gate:

Avatar portraits (migration 052): the avatar editor gains "Upload
photo" - the renderer downscales the chosen picture to a ~96px cover
crop and stores it as a small data URL (PNG keeps transparency, the
rest compresses to JPEG), so the snapshot stays light and the CSP needs
nothing new. The portrait wins over emoji and initials everywhere the
shared Avatar renders (chat, org chart, office card, sidebar), and
"Remove photo" falls back cleanly. The main process validates scheme
and size (base64 image data URL, <=300k chars) so a rogue caller cannot
re-bloat the snapshot. e2e uploads a real PNG through the profile
dialog and sees the portrait land in the sidebar.

Global pause (migration 053): when agents start talking in circles,
one topbar toggle - visible on every page - pulls the handbrake.
app_state.autorun_paused survives restarts; the queue worker checks it
each tick and neither enqueues nor claims while set (the run already in
flight finishes). Queued work simply waits and the paused state renders
as an amber "resume" pill. A unit test drives the real worker: paused
leaves a queued run untouched through many ticks, resuming claims it
within moments; e2e flips the toggle in the live shell.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-09 00:09:02 -04:00
1 parent 78e88807da
commit cb71afa6b7
25 files changed
+373 -23

No files matched your search

+24
View File
@@ -101,6 +101,20 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
await page.locator(".modal").getByRole("button", { name: "Save" }).click();
await expect(page.getByRole("button", { name: "Edit profile" })).toContainText("Dana Zhang");
await expect(page.getByRole("button", { name: "Edit profile" }).locator(".avatar")).toContainText("🎯");
// Uploading a photo replaces the emoji tile with the portrait everywhere.
await page.getByRole("button", { name: "Edit profile" }).click();
await page.getByLabel("Avatar image file").setInputFiles({
name: "portrait.png",
mimeType: "image/png",
buffer: Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
"base64",
),
});
await expect(page.locator(".modal .avatar img")).toBeVisible();
await page.locator(".modal").getByRole("button", { name: "Save" }).click();
await expect(page.getByRole("button", { name: "Edit profile" }).locator(".avatar img")).toBeVisible();
await page.getByRole("button", { name: "Work", exact: true }).click();
await page.getByRole("button", { name: "New task" }).click();
await page.getByLabel("Title").fill("Production deploy");
@@ -170,6 +184,16 @@ test("onboards, hires, and creates work in the Electron shell", async () => {
});
expect(skillSlugs).toEqual(["engineering-backend-architect", "humanizer"]);
// The global pause toggle lives in the topbar on every page.
const pauseToggle = page.getByRole("button", { name: "Pause agents" });
await expect(pauseToggle).toBeVisible();
await pauseToggle.click();
const pausedToggle = page.getByRole("button", { name: "Agents paused" });
await expect(pausedToggle).toBeVisible();
await expect(pausedToggle).toHaveAttribute("aria-pressed", "true");
await pausedToggle.click();
await expect(page.getByRole("button", { name: "Pause agents" })).toHaveAttribute("aria-pressed", "false");
// Virtual office page
await page.getByRole("button", { name: "Office" }).click();
await expect(page.locator("canvas")).toBeVisible();
+84
View File
@@ -0,0 +1,84 @@
import { randomUUID } from "node:crypto";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
import type { BrowserWindow } from "electron";
import { DatabaseConnection } from "../../src/main/database/connection";
import { OrganizationService } from "../../src/main/services/organization-service";
import { SnapshotService } from "../../src/main/services/snapshot-service";
import { isAutonomousPaused, startTaskQueueWorker } from "../../src/main/app/task-queue-worker";
const fakeWindow = {
isDestroyed: () => false,
webContents: { send: () => undefined, isDestroyed: () => false },
} as unknown as BrowserWindow;
function waitFor(predicate: () => boolean, timeoutMs: number): Promise<boolean> {
return new Promise((resolve) => {
const startedAt = Date.now();
const poll = () => {
if (predicate()) return resolve(true);
if (Date.now() - startedAt > timeoutMs) return resolve(false);
setTimeout(poll, 20);
};
poll();
});
}
describe("global autonomous pause", () => {
test("the handbrake parks the queue worker and resuming releases it", async () => {
const connection = new DatabaseConnection(join(tmpdir(), `ac-pause-${randomUUID()}.db`));
const organization = new OrganizationService(connection.db);
const snapshot = new SnapshotService(connection.db);
let orgId = "";
let runId = "";
connection.transaction(() => {
orgId = organization.createOrganization({ organizationName: "OrbitOps", employeeName: "Yang", mission: "m", goal: "g" });
const providerId = (connection.db.prepare("select id from providers where org_id = ? limit 1").get(orgId) as { id: string }).id;
const agentId = organization.createAgent({
orgId, name: "Ada", role: "Engineer", department: "Engineering",
modelProviderId: providerId, modelId: "gpt-5.4", budgetCents: 1000, managerAgentId: null,
});
const taskId = organization.createTask({
orgId, title: "Queued work", description: "Waits for the engine.", status: "backlog",
assigneeAgentId: agentId, risk: "low", projectId: null, goalId: null, workspaceId: null,
priority: "normal", requiresFilesystem: false,
});
const serviceIdentityId = (connection.db.prepare("select id from service_identities where org_id = ? and agent_id = ?")
.get(orgId, agentId) as { id: string }).id;
runId = `run_pause_${randomUUID().slice(0, 8)}`;
connection.db.prepare(`
insert into task_runs(
id, org_id, task_id, agent_id, provider_id, provider_request_id,
execution_context_id, service_identity_id, workspace_mount_json, secret_scope_json,
trigger, state, created_at, started_at, completed_at, error_message
) values (?, ?, ?, ?, ?, null, ?, ?, '[]', '{}', 'heartbeat', 'queued', ?, null, null, null)
`).run(runId, orgId, taskId, agentId, providerId, `ctx_${runId}`, serviceIdentityId, new Date().toISOString());
organization.setAutonomousPaused(true);
});
expect(isAutonomousPaused(connection)).toBe(true);
expect(snapshot.load().autonomousPaused).toBe(true);
const runState = () =>
(connection.db.prepare("select state from task_runs where id = ?").get(runId) as { state: string }).state;
const stop = startTaskQueueWorker({ connection, mainWindow: fakeWindow, intervalMs: 10 });
try {
// Paused: many ticks pass and the queued run is never claimed.
const claimedWhilePaused = await waitFor(() => runState() !== "queued", 150);
expect(claimedWhilePaused).toBe(false);
// Resume: the very next ticks claim it (execution then fails on the
// unconfigured provider, which is fine - leaving "queued" proves the
// engine moved).
connection.transaction(() => organization.setAutonomousPaused(false));
expect(snapshot.load().autonomousPaused).toBe(false);
const claimedAfterResume = await waitFor(() => runState() !== "queued", 2_000);
expect(claimedAfterResume).toBe(true);
} finally {
stop();
}
connection.close();
});
});
+1 -1
View File
@@ -24,7 +24,7 @@ describe("domain services", () => {
const connection = createConnection();
const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>;
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]);
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53]);
connection.close();
});
+1 -1
View File
@@ -8,7 +8,7 @@ function agent(id: string, department: string): AgentRecord {
modelProviderId: null, modelId: null, budgetCents: 0, managerAgentId: null,
title: id, roleLevel: "ic", modelStrategy: "single", workspaceId: null,
heartbeatEnabled: false, heartbeatIntervalSec: 0, lifecycle: "active",
avatarEmoji: null, avatarColor: null, createdAt: "2026-01-01",
avatarEmoji: null, avatarColor: null, avatarImage: null, createdAt: "2026-01-01",
};
}
+31
View File
@@ -122,4 +122,35 @@ describe("identity editing", () => {
.toThrow("Display name is required.");
h.connection.close();
});
test("stores uploaded avatar portraits and rejects anything but small data-URL images", () => {
const h = harness();
const portrait = `data:image/png;base64,${"A".repeat(120)}`;
h.connection.transaction(() => h.organization.updateEmployeeProfile({
orgId: h.orgId, employeeId: h.employeeId, displayName: "Yang", avatarImage: portrait,
}));
expect((h.connection.db.prepare("select avatar_image from employees where id = ?").get(h.employeeId) as { avatar_image: string }).avatar_image)
.toBe(portrait);
// Clearing falls back to emoji/initials.
h.connection.transaction(() => h.organization.updateEmployeeProfile({
orgId: h.orgId, employeeId: h.employeeId, displayName: "Yang", avatarImage: "",
}));
expect((h.connection.db.prepare("select avatar_image from employees where id = ?").get(h.employeeId) as { avatar_image: string | null }).avatar_image)
.toBeNull();
// Wrong scheme and oversized payloads are caller bugs, loudly rejected.
expect(() => h.organization.updateEmployeeProfile({
orgId: h.orgId, employeeId: h.employeeId, displayName: "Yang", avatarImage: "https://example.com/me.png",
})).toThrow("base64 data URL");
expect(() => h.organization.updateEmployeeProfile({
orgId: h.orgId, employeeId: h.employeeId, displayName: "Yang", avatarImage: `data:image/png;base64,${"A".repeat(300_001)}`,
})).toThrow("too large");
// Agents take portraits through the same rules.
h.connection.transaction(() => h.organization.updateAgent({
orgId: h.orgId, agentId: h.agentId, role: "CEO", title: "CEO", roleLevel: "c_suite",
department: "Executive", budgetCents: 25000, modelProviderId: null, modelId: null, avatarImage: portrait,
}));
expect((h.connection.db.prepare("select avatar_image from agents where id = ?").get(h.agentId) as { avatar_image: string }).avatar_image)
.toBe(portrait);
h.connection.close();
});
});