moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { buildServer } from '../../src/index.js';
import { users, teams, teamMembers, teamResources, agentJobs, tasks, messages } from '../../src/db/schema.js';
import { sql, eq } from 'drizzle-orm';
import { HiveMindAgent } from '../../src/daemons/hive-mind.js';
describe('Hive Mind Agent (Task 3.20)', () => {
let server: Awaited<ReturnType<typeof buildServer>>;
let ownerId: string;
let memberId: string;
let teamId: string;
let hiveMind: HiveMindAgent;
beforeAll(async () => {
server = await buildServer();
// Clean up leftover test data
await server.db.execute(sql`DELETE FROM messages WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM agent_jobs WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM tasks WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_resources WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'hivetest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'hive_%'`);
// Create test users
const [owner] = await server.db.insert(users).values({
clerkId: 'hive_owner',
displayName: 'Hive Owner',
email: 'hive_owner@test.com',
}).returning();
ownerId = owner.id;
const [member] = await server.db.insert(users).values({
clerkId: 'hive_member',
displayName: 'Hive Member',
email: 'hive_member@test.com',
}).returning();
memberId = member.id;
// Create team
const [team] = await server.db.insert(teams).values({
name: 'Hive Test Team',
slug: 'hivetest-team',
ownerId,
}).returning();
teamId = team.id;
await server.db.insert(teamMembers).values([
{ teamId, userId: ownerId, role: 'owner' },
{ teamId, userId: memberId, role: 'member' },
]);
// Seed data: completed jobs
await server.db.insert(agentJobs).values(
Array.from({ length: 5 }, (_, i) => ({
teamId,
userId: ownerId,
jobType: 'chat',
status: 'completed',
input: { prompt: `job ${i}` },
completedAt: new Date(),
})),
);
// Seed data: tasks with similar titles by different users (duplicate work)
await server.db.insert(tasks).values([
{ teamId, title: 'Setup CI/CD pipeline for backend', status: 'done', createdBy: ownerId },
{ teamId, title: 'Setup CI/CD pipeline for frontend', status: 'done', createdBy: memberId },
{ teamId, title: 'Write unit tests', status: 'done', createdBy: ownerId },
]);
// Seed data: team resources with ratings
await server.db.insert(teamResources).values([
{
teamId,
resourceType: 'prompt_template',
name: 'Best Code Review Prompt',
description: 'A highly rated code review template',
config: {},
sharedBy: ownerId,
rating: 4.5,
},
{
teamId,
resourceType: 'skill',
name: 'Low Rated Skill',
description: 'Not very useful',
config: {},
sharedBy: memberId,
rating: 1.0,
},
]);
hiveMind = new HiveMindAgent(server.db);
});
afterAll(async () => {
await server.db.execute(sql`DELETE FROM messages WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM agent_jobs WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM tasks WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_resources WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'hivetest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'hivetest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'hive_%'`);
await server.close();
});
it('generates weekly digest with metrics', async () => {
const { digest, messageId } = await hiveMind.generateWeeklyDigest(teamId);
expect(digest).toBeTruthy();
expect(digest.metrics.jobsCompleted).toBe(5);
expect(digest.metrics.tasksCompleted).toBe(3);
expect(digest.metrics.resourcesShared).toBe(2);
expect(digest.period.from).toBeInstanceOf(Date);
expect(digest.period.to).toBeInstanceOf(Date);
expect(messageId).toBeTruthy();
});
it('detects duplicate work across team members', async () => {
const { digest } = await hiveMind.generateWeeklyDigest(teamId);
// "Setup CI/CD pipeline" tasks by different users should be detected
expect(digest.duplicateWork.length).toBeGreaterThan(0);
const duplicate = digest.duplicateWork[0];
expect(duplicate.users.length).toBe(2);
expect(duplicate.titles.length).toBe(2);
});
it('identifies best practices from high-rated resources', async () => {
const { digest } = await hiveMind.generateWeeklyDigest(teamId);
// Only the high-rated resource (4.5 >= 3.0) should appear
expect(digest.bestPractices.length).toBe(1);
expect(digest.bestPractices[0].name).toBe('Best Code Review Prompt');
expect(digest.bestPractices[0].rating).toBe(4.5);
});
it('generates recommendations based on findings', async () => {
const { digest } = await hiveMind.generateWeeklyDigest(teamId);
// Should have duplicate work recommendation
expect(digest.recommendations.length).toBeGreaterThan(0);
expect(digest.recommendations.some(r => r.includes('duplicate'))).toBe(true);
// Should have best practices recommendation
expect(digest.recommendations.some(r => r.includes('highly-rated'))).toBe(true);
});
it('broadcasts digest as a waggle dance message', async () => {
const { messageId } = await hiveMind.generateWeeklyDigest(teamId);
// Verify the message was stored
const [msg] = await server.db.select().from(messages)
.where(eq(messages.id, messageId));
expect(msg).toBeTruthy();
expect(msg.type).toBe('broadcast');
expect(msg.subtype).toBe('discovery');
const digestContent = msg.content as { type: string; digest: unknown };
expect(digestContent.type).toBe('weekly_digest');
expect(digestContent.digest).toBeTruthy();
});
});

View File

@@ -0,0 +1,184 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import type { FastifyRequest, FastifyReply } from 'fastify';
import { buildServer } from '../../src/index.js';
import { users, teams, teamMembers, teamResources, agents, scoutFindings } from '../../src/db/schema.js';
import { sql, eq } from 'drizzle-orm';
import { ScoutAgent } from '../../src/daemons/scout.js';
describe('Scout Agent (Task 3.18)', () => {
let server: Awaited<ReturnType<typeof buildServer>>;
let userId: string;
let teamId: string;
let scout: ScoutAgent;
beforeAll(async () => {
server = await buildServer();
// Clean up leftover test data
await server.db.execute(sql`DELETE FROM scout_findings WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'scout_%')`);
await server.db.execute(sql`DELETE FROM agents WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'scout_%')`);
await server.db.execute(sql`DELETE FROM team_resources WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'scouttest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'scout_%'`);
// Create test user
const [user] = await server.db.insert(users).values({
clerkId: 'scout_user1',
displayName: 'Scout User',
email: 'scout_user1@test.com',
}).returning();
userId = user.id;
// Create team
const [team] = await server.db.insert(teams).values({
name: 'Scout Test Team',
slug: 'scouttest-team',
ownerId: userId,
}).returning();
teamId = team.id;
// Add membership with interests
await server.db.insert(teamMembers).values({
teamId,
userId,
role: 'owner',
interests: ['python', 'data-science'],
});
// Create an agent with tools for the user
await server.db.insert(agents).values({
userId,
teamId,
name: 'TestAgent',
tools: ['web_search', 'code_review'],
});
// Create team resources
await server.db.insert(teamResources).values([
{
teamId,
resourceType: 'skill',
name: 'Python Data Analyzer',
description: 'A skill for analyzing data with Python',
config: {},
sharedBy: userId,
},
{
teamId,
resourceType: 'prompt_template',
name: 'Code Review Template',
description: 'Template for code reviews',
config: {},
sharedBy: userId,
},
]);
scout = new ScoutAgent(server.db);
// Override auth handler
server._authHandler.fn = async function (request: FastifyRequest, reply: FastifyReply) {
const testUserId = request.headers['x-test-user-id'] as string;
if (!testUserId) {
return reply.code(401).send({ error: 'Missing x-test-user-id header' });
}
request.userId = testUserId;
request.clerkId = 'test';
};
});
afterAll(async () => {
await server.db.execute(sql`DELETE FROM scout_findings WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'scout_%')`);
await server.db.execute(sql`DELETE FROM agents WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'scout_%')`);
await server.db.execute(sql`DELETE FROM team_resources WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'scouttest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'scouttest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'scout_%'`);
await server.close();
});
it('scan creates findings from team resources', async () => {
const findings = await scout.scan(userId, teamId);
expect(findings.length).toBeGreaterThanOrEqual(2);
expect(findings.every((f) => f.status === 'new')).toBe(true);
expect(findings.some((f) => f.title.includes('Python Data Analyzer'))).toBe(true);
expect(findings.some((f) => f.title.includes('Code Review Template'))).toBe(true);
});
it('relevance scoring boosts based on user interests', async () => {
// The Python Data Analyzer should be boosted because user has 'python' interest
const findings = await scout.listFindings(userId);
const pythonFinding = findings.find((f) => f.title.includes('Python'));
const templateFinding = findings.find((f) => f.title.includes('Code Review'));
expect(pythonFinding).toBeTruthy();
expect(templateFinding).toBeTruthy();
// Python finding should have higher score (base 0.5 + 0.3 interest boost)
expect(pythonFinding!.relevanceScore).toBeGreaterThan(templateFinding!.relevanceScore);
});
it('adopt updates status', async () => {
const findings = await scout.listFindings(userId);
const finding = findings[0];
const updated = await scout.adopt(finding.id);
expect(updated).toBeTruthy();
expect(updated!.status).toBe('adopted');
});
it('dismiss updates status', async () => {
const findings = await scout.listFindings(userId);
const newFinding = findings.find((f) => f.status === 'new');
expect(newFinding).toBeTruthy();
const updated = await scout.dismiss(newFinding!.id);
expect(updated).toBeTruthy();
expect(updated!.status).toBe('dismissed');
});
it('dismissed finding not resurfaced in future scans', async () => {
// Clear all non-dismissed findings
const allFindings = await scout.listFindings(userId);
const dismissed = allFindings.filter((f) => f.status === 'dismissed');
expect(dismissed.length).toBeGreaterThan(0);
// Run scan again — dismissed titles should not reappear as new
const newFindings = await scout.scan(userId, teamId);
const dismissedTitles = dismissed.map((f) => f.title);
const resurfaced = newFindings.filter((f) => dismissedTitles.includes(f.title));
expect(resurfaced.length).toBe(0);
});
it('GET /api/scout/findings returns findings for user', async () => {
const response = await server.inject({
method: 'GET',
url: '/api/scout/findings',
headers: { 'x-test-user-id': userId },
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(Array.isArray(body)).toBe(true);
expect(body.length).toBeGreaterThan(0);
});
it('PATCH /api/scout/findings/:id rejects invalid status', async () => {
const findings = await scout.listFindings(userId);
const finding = findings[0];
const response = await server.inject({
method: 'PATCH',
url: `/api/scout/findings/${finding.id}`,
headers: { 'x-test-user-id': userId },
payload: { status: 'invalid' },
});
expect(response.statusCode).toBe(400);
});
});

View File

@@ -0,0 +1,130 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { buildServer } from '../../src/index.js';
import { users, teams, teamMembers, agentJobs, agentAuditLog } from '../../src/db/schema.js';
import { sql } from 'drizzle-orm';
import { SubconsciousAgent } from '../../src/daemons/subconscious.js';
describe('Subconscious Agent (Task 3.19)', () => {
let server: Awaited<ReturnType<typeof buildServer>>;
let userId: string;
let teamId: string;
let subconscious: SubconsciousAgent;
beforeAll(async () => {
server = await buildServer();
// Clean up leftover test data
await server.db.execute(sql`DELETE FROM agent_audit_log WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'subcon_%')`);
await server.db.execute(sql`DELETE FROM agent_jobs WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'subcon_%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'subcontest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'subcon_%'`);
// Create test user
const [user] = await server.db.insert(users).values({
clerkId: 'subcon_user1',
displayName: 'Subconscious User',
email: 'subcon_user1@test.com',
}).returning();
userId = user.id;
// Create team
const [team] = await server.db.insert(teams).values({
name: 'Subconscious Test Team',
slug: 'subcontest-team',
ownerId: userId,
}).returning();
teamId = team.id;
await server.db.insert(teamMembers).values({
teamId,
userId,
role: 'owner',
});
subconscious = new SubconsciousAgent(server.db);
});
afterAll(async () => {
await server.db.execute(sql`DELETE FROM agent_audit_log WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'subcon_%')`);
await server.db.execute(sql`DELETE FROM agent_jobs WHERE user_id IN (SELECT id FROM users WHERE clerk_id LIKE 'subcon_%')`);
await server.db.execute(sql`DELETE FROM team_members WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_requests WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_overrides WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM team_capability_policies WHERE team_id IN (SELECT id FROM teams WHERE slug LIKE 'subcontest-%')`);
await server.db.execute(sql`DELETE FROM teams WHERE slug LIKE 'subcontest-%'`);
await server.db.execute(sql`DELETE FROM users WHERE clerk_id LIKE 'subcon_%'`);
await server.close();
});
it('shouldReflect returns false when under threshold', async () => {
// No completed jobs yet
const result = await subconscious.shouldReflect(userId);
expect(result).toBe(false);
});
it('shouldReflect returns true after N completed jobs', async () => {
// Insert 10 completed jobs (SUBCONSCIOUS_INTERACTION_THRESHOLD = 10)
const jobValues = Array.from({ length: 10 }, (_, i) => ({
teamId,
userId,
jobType: 'chat',
status: 'completed',
input: { prompt: `test ${i}` },
completedAt: new Date(),
}));
await server.db.insert(agentJobs).values(jobValues);
const result = await subconscious.shouldReflect(userId);
expect(result).toBe(true);
});
it('reflect creates audit entry', async () => {
const { auditEntry, insights } = await subconscious.reflect(userId);
expect(auditEntry).toBeTruthy();
expect(auditEntry.agentName).toBe('subconscious');
expect(auditEntry.actionType).toBe('subconscious_reflection');
expect(auditEntry.description).toContain('Reflected on');
expect(Array.isArray(insights)).toBe(true);
});
it('detects repeated job types pattern', async () => {
// Insert 5 more jobs of same type to trigger pattern detection
const repeatedJobs = Array.from({ length: 5 }, (_, i) => ({
teamId,
userId,
jobType: 'task',
status: 'completed',
input: { prompt: `repeated ${i}` },
completedAt: new Date(),
}));
await server.db.insert(agentJobs).values(repeatedJobs);
const { insights } = await subconscious.reflect(userId);
// Should find a prompt_change insight for the repeated 'chat' type (10 jobs)
const promptInsight = insights.find(i => i.type === 'prompt_change');
expect(promptInsight).toBeTruthy();
expect(promptInsight!.description).toContain('executed');
expect(promptInsight!.description).toContain('times recently');
});
it('prompt change insight requires approval in audit entry', async () => {
// The previous reflect should have created an audit entry with requiresApproval
const entries = await server.db.select().from(agentAuditLog)
.where(sql`${agentAuditLog.userId} = ${userId} AND ${agentAuditLog.actionType} = 'subconscious_reflection'`);
// Find one that has insights with prompt_change
const withApproval = entries.find(e => {
const state = e.afterState as { insights?: Array<{ type: string }> } | null;
return state?.insights?.some((i) => i.type === 'prompt_change');
});
expect(withApproval).toBeTruthy();
expect(withApproval!.requiresApproval).toBe(true);
});
});