This commit is contained in:
ZacharyZhang-NY committed 2026-05-15 19:40:20 -04:00
1 parent 2ab001b18a
commit 810e2d5967
28 files changed
+1015 -85

No files matched your search

+2 -2
View File
@@ -8,7 +8,7 @@ describe("buildCapabilityRegistrySection", () => {
{ name: "React Standards", slug: "react-standards", description: "Use when building React UI" },
],
companyWorkspaces: [
{ name: "Primary", localPath: "/tmp/workspace" },
{ id: "workspace-1", name: "Primary", localPath: "/tmp/workspace" },
],
allConnectors: [
{ id: "openai-codex", label: "Codex", status: "ready", enabledModels: ["gpt-5.4"] },
@@ -37,7 +37,7 @@ describe("buildCapabilityRegistrySection", () => {
expect(section).toContain("# Company Capability Registry");
expect(section).toContain("React Standards (`react-standards`)");
expect(section).toContain("Primary: /tmp/workspace");
expect(section).toContain("Primary (`workspace-1`): /tmp/workspace");
expect(section).toContain("Codex (`openai-codex`) — models: gpt-5.4");
expect(section).toContain("architecture_decision");
});
+2
View File
@@ -482,6 +482,7 @@ describe("AppDatabase", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
db.close();
@@ -496,6 +497,7 @@ describe("AppDatabase", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
});
@@ -176,6 +176,7 @@ describe("legacy to v56 baseline migration", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
expect(appDb.queryAll<{ companyId: string }>("select company_id as companyId from secrets where id = 'secret-1'")[0])
.toEqual({ companyId: "company-1" });
@@ -210,6 +211,7 @@ describe("legacy to v56 baseline migration", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
expect(appDb.queryAll("select id from budget_policies where scope_type = 'agent' and scope_id = 'agent-1'"))
.toHaveLength(1);
@@ -235,6 +237,7 @@ describe("legacy to v56 baseline migration", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
} finally {
appDb.close();
@@ -394,6 +397,7 @@ describe("legacy to v56 baseline migration", () => {
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
} finally {
appDb.close();
+101
View File
@@ -0,0 +1,101 @@
import { join } from "node:path";
import { tmpdir } from "node:os";
import { mkdtemp, rm } from "node:fs/promises";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ConnectorId } from "@shared/types";
vi.mock("electron", () => ({
safeStorage: {
isEncryptionAvailable: () => false,
getSelectedStorageBackend: () => "basic_text",
encryptString: (value: string) => Buffer.from(value, "utf8"),
decryptString: (value: Buffer) => value.toString("utf8"),
},
}));
import { AppDatabase } from "../../src/main/database";
import { CONNECTORS } from "../../src/main/connector-definitions";
import { resolveModels } from "../../src/main/model-resolver";
import { pickFromCandidates } from "../../src/main/model-dispatch";
describe("model capability seed", () => {
let tempDir: string;
let db: AppDatabase;
beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), "agentcompany-model-capabilities-"));
db = new AppDatabase(tempDir);
db.init();
});
afterEach(async () => {
db.close();
await rm(tempDir, { recursive: true, force: true });
});
it("keeps catalog-seeded rows current on existing profiles without overwriting user edits", () => {
db.core.modelCapabilities.upsertModelCapability({
provider: "zai",
modelId: "glm-4.6",
reasoning: 2,
coding: 2,
writing: 2,
tier: "trivial",
costPerMtokOut: 99,
});
db.core.modelCapabilities.deleteModelCapability("zai", "glm-5.1");
db.close();
db = new AppDatabase(tempDir);
db.init();
expect(db.core.modelCapabilities.getModelCapability("zai", "glm-5.1")).toMatchObject({
provider: "zai",
modelId: "glm-5.1",
reasoning: 5,
coding: 4,
writing: 4,
tier: "hard",
});
expect(db.core.modelCapabilities.getModelCapability("zai", "glm-4.6")).toMatchObject({
reasoning: 2,
coding: 2,
writing: 2,
tier: "trivial",
costPerMtokOut: 99,
});
});
it("seeds a capability row for every static connector catalog model", () => {
const seeded = new Set(
db.core.modelCapabilities
.listModelCapabilities()
.map((entry) => `${entry.provider}::${entry.modelId}`),
);
const expected = new Set<string>();
for (const connector of CONNECTORS) {
for (const model of resolveModels(connector.provider)) {
expected.add(`${connector.provider}::${model.id}`);
}
if (connector.auth.type !== "local" && connector.defaultModel.trim()) {
expected.add(`${connector.provider}::${connector.defaultModel}`);
}
}
expect(expected.size).toBeGreaterThan(700);
for (const key of expected) {
expect(seeded.has(key), key).toBe(true);
}
});
it("uses the same catalog seed path for hard reasoning picks", () => {
const winner = pickFromCandidates(
{ domain: "reasoning", requiredTier: "hard" },
[{ provider: "zai", connectorId: "zai" as ConnectorId, modelId: "glm-5.1" }],
db.core.modelCapabilities.listModelCapabilities(),
);
expect(winner?.modelId).toBe("glm-5.1");
});
});
@@ -18,6 +18,7 @@ vi.mock("electron", () => ({
}));
import { AppDatabase } from "../../src/main/database";
import { migrateFilesystemTaskWorkspacesV60 } from "../../src/main/database/migrations/v60-filesystem-task-workspaces";
const tempDirs: string[] = [];
@@ -116,16 +117,74 @@ describe("legacy to v56 baseline migration", () => {
{ id: "task-inflight", requiresFilesystem: 1 },
]);
const migrations = appDb.queryAll<{ version: number; name: string }>(
"select version, name from schema_migrations where version in (56, 57, 58, 59) order by version",
"select version, name from schema_migrations where version in (56, 57, 58, 59, 60) order by version",
);
expect(migrations).toEqual([
{ version: 56, name: "baseline_v56" },
{ version: 57, name: "task_leases_runtime_ownership" },
{ version: 58, name: "approval_policy_table" },
{ version: 59, name: "social_company_scope" },
{ version: 60, name: "filesystem_task_workspaces" },
]);
} finally {
appDb.close();
}
});
it("backfills active filesystem tasks in single-workspace companies", async () => {
const profileDir = await createTempDir("agentcompany-fs-workspace-v60-");
const rawDb = new DatabaseSync(join(profileDir, "profile.sqlite"));
const ts = "2026-05-15T00:00:00.000Z";
try {
rawDb.exec(`
create table schema_migrations (version integer primary key, name text not null, applied_at text not null);
create table workspaces (
id text primary key,
company_id text not null,
project_id text,
is_primary integer not null,
created_at text not null
);
create table agents (
id text primary key,
company_id text not null,
workspace_id text
);
create table tasks (
id text primary key,
company_id text not null,
project_id text,
assignee_agent_id text,
workspace_id text,
status text not null,
requires_filesystem integer not null,
updated_at text not null
);
`);
rawDb
.prepare("insert into workspaces (id, company_id, project_id, is_primary, created_at) values (?, ?, ?, ?, ?)")
.run("workspace-1", "company-1", null, 1, ts);
rawDb
.prepare("insert into agents (id, company_id, workspace_id) values (?, ?, ?)")
.run("agent-1", "company-1", null);
rawDb
.prepare("insert into tasks (id, company_id, project_id, assignee_agent_id, workspace_id, status, requires_filesystem, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?)")
.run("task-stuck", "company-1", "project-1", "agent-1", null, "todo", 1, ts);
rawDb
.prepare("insert into tasks (id, company_id, project_id, assignee_agent_id, workspace_id, status, requires_filesystem, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?)")
.run("task-cognitive", "company-1", "project-1", "agent-1", null, "todo", 0, ts);
migrateFilesystemTaskWorkspacesV60(rawDb);
const rows = rawDb
.prepare("select id, workspace_id as workspaceId from tasks order by id")
.all() as Array<{ id: string; workspaceId: string | null }>;
expect(rows).toEqual([
{ id: "task-cognitive", workspaceId: null },
{ id: "task-stuck", workspaceId: "workspace-1" },
]);
} finally {
rawDb.close();
}
});
});
+108
View File
@@ -0,0 +1,108 @@
import { join } from "node:path";
import { tmpdir } from "node:os";
import { mkdtemp, rm } from "node:fs/promises";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("electron", () => ({
safeStorage: {
isEncryptionAvailable: () => false,
getSelectedStorageBackend: () => "basic_text",
encryptString: (value: string) => Buffer.from(value, "utf8"),
decryptString: (value: Buffer) => value.toString("utf8"),
},
}));
import { AppDatabase } from "../../src/main/database";
import { resolveTaskWorkspaceBinding } from "../../src/main/task-workspace-binding";
describe("resolveTaskWorkspaceBinding", () => {
let tempDir: string;
let db: AppDatabase;
beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), "agentcompany-task-workspace-binding-"));
db = new AppDatabase(tempDir);
db.init();
});
afterEach(async () => {
db.close();
await rm(tempDir, { recursive: true, force: true });
});
it("binds filesystem tasks to the creator agent workspace", () => {
const { companyId, workspaceId, agentId } = seedCompany(db);
const result = resolveTaskWorkspaceBinding(db, {
companyId,
requiresFilesystem: true,
creatorAgentId: agentId,
});
expect(result).toEqual({ ok: true, workspaceId, source: "creator" });
});
it("binds filesystem tasks to the only company workspace", () => {
const { companyId, workspaceId } = seedCompany(db, { agentWorkspace: false });
const result = resolveTaskWorkspaceBinding(db, {
companyId,
requiresFilesystem: true,
});
expect(result).toEqual({ ok: true, workspaceId, source: "single_company" });
});
it("rejects filesystem tasks when the workspace is ambiguous", () => {
const { companyId } = seedCompany(db, { agentWorkspace: false });
db.core.workspaces.saveWorkspace({
companyId,
name: "Second",
localPath: "/tmp/second",
repoUrl: "",
repoRef: "",
isPrimary: false,
});
const result = resolveTaskWorkspaceBinding(db, {
companyId,
requiresFilesystem: true,
});
expect(result.ok).toBe(false);
});
});
function seedCompany(db: AppDatabase, options: { agentWorkspace?: boolean } = {}) {
const companyId = db.core.company.saveCompany({
name: "Workspace Binding Co",
description: "",
status: "active",
});
db.core.connectors.saveConnector({
id: "openai-codex",
model: "gpt-5",
envBindingText: "",
notes: "",
});
const workspaceId = db.core.workspaces.saveWorkspace({
companyId,
name: "Default",
localPath: "/tmp/default",
repoUrl: "",
repoRef: "",
isPrimary: true,
});
const agentId = db.core.agents.saveAgent({
companyId,
name: "CEO",
role: "CEO",
title: "CEO",
status: "active",
reportsTo: null,
connectorId: "openai-codex",
workspaceId: options.agentWorkspace === false ? null : workspaceId,
model: null,
capabilities: "Leadership",
budgetMonthlyUsd: 100,
});
return { companyId, workspaceId, agentId };
}