feat(#93): implement agent spawn via federation
Implements FED-010: Agent Spawn via Federation feature that enables spawning and managing Claude agents on remote federated Mosaic Stack instances via COMMAND message type. Features: - Federation agent command types (spawn, status, kill) - FederationAgentService for handling agent operations - Integration with orchestrator's agent spawner/lifecycle services - API endpoints for spawning, querying status, and killing agents - Full command routing through federation COMMAND infrastructure - Comprehensive test coverage (12/12 tests passing) Architecture: - Hub → Spoke: Spawn agents on remote instances - Command flow: FederationController → FederationAgentService → CommandService → Remote Orchestrator - Response handling: Remote orchestrator returns agent status/results - Security: Connection validation, signature verification Files created: - apps/api/src/federation/types/federation-agent.types.ts - apps/api/src/federation/federation-agent.service.ts - apps/api/src/federation/federation-agent.service.spec.ts Files modified: - apps/api/src/federation/command.service.ts (agent command routing) - apps/api/src/federation/federation.controller.ts (agent endpoints) - apps/api/src/federation/federation.module.ts (service registration) - apps/orchestrator/src/api/agents/agents.controller.ts (status endpoint) - apps/orchestrator/src/api/agents/agents.module.ts (lifecycle integration) Testing: - 12/12 tests passing for FederationAgentService - All command service tests passing - TypeScript compilation successful - Linting passed Refs #93 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ Added comprehensive team support for workspace collaboration:
|
||||
#### Schema Changes
|
||||
|
||||
**New Enum:**
|
||||
|
||||
```prisma
|
||||
enum TeamMemberRole {
|
||||
OWNER
|
||||
@@ -23,6 +24,7 @@ enum TeamMemberRole {
|
||||
```
|
||||
|
||||
**New Models:**
|
||||
|
||||
```prisma
|
||||
model Team {
|
||||
id String @id @default(uuid())
|
||||
@@ -43,6 +45,7 @@ model TeamMember {
|
||||
```
|
||||
|
||||
**Updated Relations:**
|
||||
|
||||
- `User.teamMemberships` - Access user's team memberships
|
||||
- `Workspace.teams` - Access workspace's teams
|
||||
|
||||
@@ -58,6 +61,7 @@ Implemented comprehensive RLS policies for complete tenant isolation:
|
||||
#### RLS-Enabled Tables (19 total)
|
||||
|
||||
All tenant-scoped tables now have RLS enabled:
|
||||
|
||||
- Core: `workspaces`, `workspace_members`, `teams`, `team_members`
|
||||
- Data: `tasks`, `events`, `projects`, `activity_logs`
|
||||
- Features: `domains`, `ideas`, `relationships`, `agents`, `agent_sessions`
|
||||
@@ -75,6 +79,7 @@ Three utility functions for policy evaluation:
|
||||
#### Policy Pattern
|
||||
|
||||
Consistent policy implementation across all tables:
|
||||
|
||||
```sql
|
||||
CREATE POLICY <table>_workspace_access ON <table>
|
||||
FOR ALL
|
||||
@@ -88,6 +93,7 @@ Created helper utilities for easy RLS integration in the API layer:
|
||||
**File:** `apps/api/src/lib/db-context.ts`
|
||||
|
||||
**Key Functions:**
|
||||
|
||||
- `setCurrentUser(userId)` - Set user context for RLS
|
||||
- `withUserContext(userId, fn)` - Execute function with user context
|
||||
- `withUserTransaction(userId, fn)` - Transaction with user context
|
||||
@@ -119,39 +125,39 @@ Created helper utilities for easy RLS integration in the API layer:
|
||||
### In API Routes/Procedures
|
||||
|
||||
```typescript
|
||||
import { withUserContext } from '@/lib/db-context';
|
||||
import { withUserContext } from "@/lib/db-context";
|
||||
|
||||
// Method 1: Explicit context
|
||||
export async function getTasks(userId: string, workspaceId: string) {
|
||||
return withUserContext(userId, async () => {
|
||||
return prisma.task.findMany({
|
||||
where: { workspaceId }
|
||||
where: { workspaceId },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Method 2: HOF wrapper
|
||||
import { withAuth } from '@/lib/db-context';
|
||||
import { withAuth } from "@/lib/db-context";
|
||||
|
||||
export const getTasks = withAuth(async ({ ctx, input }) => {
|
||||
return prisma.task.findMany({
|
||||
where: { workspaceId: input.workspaceId }
|
||||
where: { workspaceId: input.workspaceId },
|
||||
});
|
||||
});
|
||||
|
||||
// Method 3: Transaction
|
||||
import { withUserTransaction } from '@/lib/db-context';
|
||||
import { withUserTransaction } from "@/lib/db-context";
|
||||
|
||||
export async function createWorkspace(userId: string, name: string) {
|
||||
return withUserTransaction(userId, async (tx) => {
|
||||
const workspace = await tx.workspace.create({
|
||||
data: { name, ownerId: userId }
|
||||
data: { name, ownerId: userId },
|
||||
});
|
||||
|
||||
|
||||
await tx.workspaceMember.create({
|
||||
data: { workspaceId: workspace.id, userId, role: 'OWNER' }
|
||||
data: { workspaceId: workspace.id, userId, role: "OWNER" },
|
||||
});
|
||||
|
||||
|
||||
return workspace;
|
||||
});
|
||||
}
|
||||
@@ -254,20 +260,18 @@ SELECT * FROM workspaces; -- Should only see user 2's workspaces
|
||||
|
||||
```typescript
|
||||
// In a test file
|
||||
import { withUserContext, verifyWorkspaceAccess } from '@/lib/db-context';
|
||||
import { withUserContext, verifyWorkspaceAccess } from "@/lib/db-context";
|
||||
|
||||
describe('RLS Utilities', () => {
|
||||
it('should isolate workspaces', async () => {
|
||||
describe("RLS Utilities", () => {
|
||||
it("should isolate workspaces", async () => {
|
||||
const workspaces = await withUserContext(user1Id, async () => {
|
||||
return prisma.workspace.findMany();
|
||||
});
|
||||
|
||||
expect(workspaces.every(w =>
|
||||
w.members.some(m => m.userId === user1Id)
|
||||
)).toBe(true);
|
||||
|
||||
expect(workspaces.every((w) => w.members.some((m) => m.userId === user1Id))).toBe(true);
|
||||
});
|
||||
|
||||
it('should verify access', async () => {
|
||||
|
||||
it("should verify access", async () => {
|
||||
const hasAccess = await verifyWorkspaceAccess(userId, workspaceId);
|
||||
expect(hasAccess).toBe(true);
|
||||
});
|
||||
@@ -290,7 +294,7 @@ cd apps/api && npx prisma format
|
||||
# Create Team model migration
|
||||
npx prisma migrate dev --name add_team_model --create-only
|
||||
|
||||
# Create RLS migration
|
||||
# Create RLS migration
|
||||
npx prisma migrate dev --name add_rls_policies --create-only
|
||||
|
||||
# Apply migrations
|
||||
|
||||
Reference in New Issue
Block a user