Replaces legacy adapter ids (codex_local / claude_local / gemini_local / claude_compatible) across ~42 test files with pi-mono equivalents: - codex_local → openai-codex - claude_local → claude-pro - gemini_local → gemini-cli - claude_compatible → anthropic Hand-rewrites where fixture semantics changed: - connector-matching: TASK_TYPE_PREFERENCE ordering rebased (claude-pro first for code/content/management; google first for research) - connector-model-policy: getModels(provider) from pi-ai instead of the deleted adapter.module.models array - session-management + edge-case-session: adapter exemption removed; rotation now uniform under pi-mono - ipc-core-admin-handlers: CSV compat model replaced with pi-ai anthropic - connectors.spec: lookup by openai-codex id (catalog now starts with claude-pro) - onboarding-model-pool: listProviderModels(provider) helper; label text changed to "ChatGPT Plus/Pro (Codex)" - database integration: added vault insecure-fallback env + electron mock - command-paths: uses new @main/connector-helpers normalizeCommandValue - pi-mono-migrations: added `as string` casts for 3 negative-existence assertions verifying legacy ids were removed Drops: - pnpm-workspace.yaml packages: [] (was "packages/*"; directory empty) - obsolete CONNECTOR_STRENGTHS import; .module.models probing Verification: - pnpm typecheck → 0 errors - pnpm lint → 0 errors / 0 warnings - pnpm build:verify → 0 errors / 0 warnings - pnpm test:unit → 508/508 pass - pnpm test:integration → 96/96 pass - All 6 adapter hard gates pass (NOT FOUND / 0 matches / empty) - pi-ai / pi-agent-core / pi-coding-agent all resolved @0.67.6
228 lines
6.6 KiB
TypeScript
228 lines
6.6 KiB
TypeScript
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { randomUUID } from "node:crypto";
|
|
import { AppDatabase } from "../../src/main/database";
|
|
import { buildTaskPrompt } from "../../src/main/prompt-builder";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
async function createTempDir(prefix: string) {
|
|
const dir = await mkdtemp(join(tmpdir(), prefix));
|
|
tempDirs.push(dir);
|
|
return dir;
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
describe("skill-aware capability registry", () => {
|
|
it("autoAssignTask prefers an agent whose assigned skill matches the task, even when capability text is generic", async () => {
|
|
const profileDir = await createTempDir("agentcompany-skill-assign-");
|
|
const db = new AppDatabase(profileDir);
|
|
db.init();
|
|
|
|
db.updateConnectorHealth({
|
|
id: "openai-codex",
|
|
status: "ready",
|
|
authState: "authenticated",
|
|
version: "test",
|
|
lastError: null,
|
|
});
|
|
|
|
const companyId = db.saveCompany({
|
|
name: "Skill Match Co",
|
|
description: "Tests skill-aware assignment",
|
|
status: "active",
|
|
});
|
|
const workspaceId = db.saveWorkspace({
|
|
companyId,
|
|
name: "Primary",
|
|
localPath: profileDir,
|
|
repoUrl: "",
|
|
repoRef: "",
|
|
isPrimary: true,
|
|
});
|
|
|
|
db.saveAgent({
|
|
companyId,
|
|
name: "Generic Engineer",
|
|
role: "Engineer",
|
|
title: "Engineer",
|
|
status: "active",
|
|
reportsTo: null,
|
|
connectorId: "openai-codex",
|
|
workspaceId,
|
|
model: null,
|
|
capabilities: "General software delivery",
|
|
budgetMonthlyUsd: 200,
|
|
});
|
|
const styledEngineerId = db.saveAgent({
|
|
companyId,
|
|
name: "Frontend Specialist",
|
|
role: "Engineer",
|
|
title: "Frontend Specialist",
|
|
status: "active",
|
|
reportsTo: null,
|
|
connectorId: "openai-codex",
|
|
workspaceId,
|
|
model: null,
|
|
capabilities: "General software delivery",
|
|
budgetMonthlyUsd: 200,
|
|
});
|
|
|
|
const skillId = randomUUID();
|
|
const now = new Date().toISOString();
|
|
db.upsertCompanySkill({
|
|
id: skillId,
|
|
companyId,
|
|
key: "frontend/react-ui",
|
|
slug: "react-ui",
|
|
name: "React UI",
|
|
description: "Build React components with hooks, Tailwind, and accessible layouts",
|
|
markdown: "# React UI\nUse when building React components with hooks and Tailwind.",
|
|
sourceType: "local_path",
|
|
sourceLocator: null,
|
|
sourceRef: null,
|
|
trustLevel: "markdown_only",
|
|
compatibility: "compatible",
|
|
fileInventory: "[]",
|
|
metadata: null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
db.assignSkillToAgent(styledEngineerId, skillId);
|
|
|
|
const taskId = db.saveTask({
|
|
companyId,
|
|
title: "Build React dashboard widget",
|
|
description: "Create an accessible React component with hooks and Tailwind styling.",
|
|
workspaceId,
|
|
priority: "high",
|
|
status: "todo",
|
|
});
|
|
|
|
const assignedId = db.autoAssignTask(taskId, companyId);
|
|
expect(assignedId).toBe(styledEngineerId);
|
|
|
|
db.close();
|
|
});
|
|
|
|
it("injects the company capability registry into the task prompt", async () => {
|
|
const profileDir = await createTempDir("agentcompany-capability-prompt-");
|
|
const db = new AppDatabase(profileDir);
|
|
db.init();
|
|
|
|
db.updateConnectorHealth({
|
|
id: "openai-codex",
|
|
status: "ready",
|
|
authState: "authenticated",
|
|
version: "test",
|
|
lastError: null,
|
|
});
|
|
|
|
const companyId = db.saveCompany({
|
|
name: "Registry Co",
|
|
description: "Tests capability registry prompt injection",
|
|
status: "active",
|
|
});
|
|
const workspaceId = db.saveWorkspace({
|
|
companyId,
|
|
name: "Primary",
|
|
localPath: profileDir,
|
|
repoUrl: "",
|
|
repoRef: "",
|
|
isPrimary: true,
|
|
});
|
|
const agentId = db.saveAgent({
|
|
companyId,
|
|
name: "CEO",
|
|
role: "CEO",
|
|
title: "Chief Executive Officer",
|
|
status: "active",
|
|
reportsTo: null,
|
|
connectorId: "openai-codex",
|
|
workspaceId,
|
|
model: null,
|
|
capabilities: "Lead the company",
|
|
budgetMonthlyUsd: 250,
|
|
});
|
|
|
|
const skillId = randomUUID();
|
|
const now = new Date().toISOString();
|
|
db.upsertCompanySkill({
|
|
id: skillId,
|
|
companyId,
|
|
key: "frontend/react-ui",
|
|
slug: "react-ui",
|
|
name: "React UI",
|
|
description: "Use when building React interfaces",
|
|
markdown: "# React UI\nUse when building React interfaces.",
|
|
sourceType: "local_path",
|
|
sourceLocator: null,
|
|
sourceRef: null,
|
|
trustLevel: "markdown_only",
|
|
compatibility: "compatible",
|
|
fileInventory: "[]",
|
|
metadata: null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
|
|
const snapshot = db.listSnapshot();
|
|
const prompt = buildTaskPrompt({
|
|
agent: db.getAgent(agentId),
|
|
apiUrl: "http://127.0.0.1:3210",
|
|
apiKey: "token",
|
|
runId: "run-1",
|
|
task: null,
|
|
companyName: "Registry Co",
|
|
companyDescription: "Tests capability registry prompt injection",
|
|
goals: [],
|
|
projects: [],
|
|
directReports: [],
|
|
chainOfCommand: [],
|
|
assignedTasks: [],
|
|
allAgents: snapshot.agents.filter((entry) => entry.companyId === companyId),
|
|
pendingApprovals: [],
|
|
tasksAwaitingReview: [],
|
|
wakeReason: "assignment",
|
|
allTasks: [],
|
|
recentComments: [],
|
|
socialAccounts: [],
|
|
allConnectors: snapshot.connectors
|
|
.filter((connector) => connector.id === "openai-codex")
|
|
.map((connector) => ({ id: connector.id, label: connector.label, status: connector.status, enabledModels: ["gpt-5.4"] })),
|
|
companySkillCatalog: db.listCompanySkills(companyId).map((skill) => ({
|
|
name: skill.name,
|
|
slug: skill.slug,
|
|
description: skill.description,
|
|
})),
|
|
companyWorkspaces: snapshot.workspaces
|
|
.filter((workspace) => workspace.companyId === companyId)
|
|
.map((workspace) => ({ name: workspace.name, localPath: workspace.localPath })),
|
|
companyDocuments: [],
|
|
skillEntries: [
|
|
{
|
|
name: "React Standards",
|
|
whenToUse: "building React UI",
|
|
allowedTools: ["read", "edit"],
|
|
effort: "high",
|
|
contextMode: "inline",
|
|
},
|
|
],
|
|
skillNames: ["React Standards"],
|
|
});
|
|
|
|
expect(prompt).toContain("# Company Capability Registry");
|
|
expect(prompt).toContain("React UI (`react-ui`)");
|
|
expect(prompt).toContain("Primary:");
|
|
expect(prompt).toContain("Ready Connectors");
|
|
expect(prompt).toContain("Your domain skills:");
|
|
|
|
db.close();
|
|
});
|
|
});
|