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:
Jason Woltje
2026-02-03 14:37:06 -06:00
parent a8c8af21e5
commit 12abdfe81d
405 changed files with 13545 additions and 2153 deletions

View File

@@ -5,6 +5,7 @@ The Activity Logging API provides comprehensive audit trail and activity trackin
## Overview
Activity logs are automatically created for:
- **CRUD Operations**: Task, event, project, and workspace modifications
- **Authentication Events**: Login, logout, password resets
- **User Actions**: Task assignments, workspace member changes
@@ -24,17 +25,17 @@ Get a paginated list of activity logs with optional filters.
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `workspaceId` | UUID | Yes | Workspace to filter by |
| `userId` | UUID | No | Filter by user who performed the action |
| `action` | ActivityAction | No | Filter by action type (CREATED, UPDATED, etc.) |
| `entityType` | EntityType | No | Filter by entity type (TASK, EVENT, etc.) |
| `entityId` | UUID | No | Filter by specific entity |
| `startDate` | ISO 8601 | No | Filter activities after this date |
| `endDate` | ISO 8601 | No | Filter activities before this date |
| `page` | Number | No | Page number (default: 1) |
| `limit` | Number | No | Items per page (default: 50, max: 100) |
| Parameter | Type | Required | Description |
| ------------- | -------------- | -------- | ---------------------------------------------- |
| `workspaceId` | UUID | Yes | Workspace to filter by |
| `userId` | UUID | No | Filter by user who performed the action |
| `action` | ActivityAction | No | Filter by action type (CREATED, UPDATED, etc.) |
| `entityType` | EntityType | No | Filter by entity type (TASK, EVENT, etc.) |
| `entityId` | UUID | No | Filter by specific entity |
| `startDate` | ISO 8601 | No | Filter activities after this date |
| `endDate` | ISO 8601 | No | Filter activities before this date |
| `page` | Number | No | Page number (default: 1) |
| `limit` | Number | No | Items per page (default: 50, max: 100) |
**Response:**
@@ -102,15 +103,15 @@ Retrieve a single activity log entry by ID.
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | UUID | Yes | Activity log ID |
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | --------------- |
| `id` | UUID | Yes | Activity log ID |
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
| Parameter | Type | Required | Description |
| ------------- | ---- | -------- | ----------------------------------------- |
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
**Response:**
@@ -156,16 +157,16 @@ Retrieve complete audit trail for a specific entity (task, event, project, etc.)
**Path Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entityType` | EntityType | Yes | Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER) |
| `entityId` | UUID | Yes | Entity ID |
| Parameter | Type | Required | Description |
| ------------ | ---------- | -------- | ------------------------------------------------------ |
| `entityType` | EntityType | Yes | Type of entity (TASK, EVENT, PROJECT, WORKSPACE, USER) |
| `entityId` | UUID | Yes | Entity ID |
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
| Parameter | Type | Required | Description |
| ------------- | ---- | -------- | ----------------------------------------- |
| `workspaceId` | UUID | Yes | Workspace ID (for multi-tenant isolation) |
**Response:**
@@ -265,6 +266,7 @@ The Activity Logging system includes an interceptor that automatically logs:
- **DELETE requests** → `DELETED` action
The interceptor extracts:
- User information from the authenticated session
- Workspace context from request
- IP address and user agent from HTTP headers
@@ -277,7 +279,7 @@ The interceptor extracts:
For custom logging scenarios, use the `ActivityService` helper methods:
```typescript
import { ActivityService } from '@/activity/activity.service';
import { ActivityService } from "@/activity/activity.service";
@Injectable()
export class TaskService {
@@ -287,12 +289,7 @@ export class TaskService {
const task = await this.prisma.task.create({ data });
// Log task creation
await this.activityService.logTaskCreated(
workspaceId,
userId,
task.id,
{ title: task.title }
);
await this.activityService.logTaskCreated(workspaceId, userId, task.id, { title: task.title });
return task;
}
@@ -302,6 +299,7 @@ export class TaskService {
### Available Helper Methods
#### Task Activities
- `logTaskCreated(workspaceId, userId, taskId, details?)`
- `logTaskUpdated(workspaceId, userId, taskId, details?)`
- `logTaskDeleted(workspaceId, userId, taskId, details?)`
@@ -309,22 +307,26 @@ export class TaskService {
- `logTaskAssigned(workspaceId, userId, taskId, assigneeId)`
#### Event Activities
- `logEventCreated(workspaceId, userId, eventId, details?)`
- `logEventUpdated(workspaceId, userId, eventId, details?)`
- `logEventDeleted(workspaceId, userId, eventId, details?)`
#### Project Activities
- `logProjectCreated(workspaceId, userId, projectId, details?)`
- `logProjectUpdated(workspaceId, userId, projectId, details?)`
- `logProjectDeleted(workspaceId, userId, projectId, details?)`
#### Workspace Activities
- `logWorkspaceCreated(workspaceId, userId, details?)`
- `logWorkspaceUpdated(workspaceId, userId, details?)`
- `logWorkspaceMemberAdded(workspaceId, userId, memberId, role)`
- `logWorkspaceMemberRemoved(workspaceId, userId, memberId)`
#### User Activities
- `logUserUpdated(workspaceId, userId, details?)`
---
@@ -338,6 +340,7 @@ All activity logs are scoped to workspaces using Row-Level Security (RLS). Users
### Data Retention
Activity logs are retained indefinitely by default. Consider implementing a retention policy based on:
- Compliance requirements
- Storage constraints
- Business needs
@@ -345,6 +348,7 @@ Activity logs are retained indefinitely by default. Consider implementing a rete
### Sensitive Data
Activity logs should NOT contain:
- Passwords or authentication tokens
- Credit card information
- Personal health information
@@ -364,9 +368,9 @@ Include enough context to understand what changed:
// Good
await activityService.logTaskUpdated(workspaceId, userId, taskId, {
changes: {
status: { from: 'NOT_STARTED', to: 'IN_PROGRESS' },
assignee: { from: null, to: 'user-456' }
}
status: { from: "NOT_STARTED", to: "IN_PROGRESS" },
assignee: { from: null, to: "user-456" },
},
});
// Less useful
@@ -376,6 +380,7 @@ await activityService.logTaskUpdated(workspaceId, userId, taskId);
### 2. Log Business-Critical Actions
Prioritize logging actions that:
- Change permissions or access control
- Delete data
- Modify billing or subscription
@@ -388,11 +393,11 @@ Use appropriate filters to reduce data transfer:
```typescript
// Efficient - filters at database level
const activities = await fetch('/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50');
const activities = await fetch("/api/activity?workspaceId=xxx&entityType=TASK&page=1&limit=50");
// Inefficient - transfers all data then filters
const activities = await fetch('/api/activity?workspaceId=xxx');
const taskActivities = activities.filter(a => a.entityType === 'TASK');
const activities = await fetch("/api/activity?workspaceId=xxx");
const taskActivities = activities.filter((a) => a.entityType === "TASK");
```
### 4. Display User-Friendly Activity Feeds
@@ -404,11 +409,11 @@ function formatActivityMessage(activity: ActivityLog) {
const { user, action, entityType, details } = activity;
switch (action) {
case 'CREATED':
case "CREATED":
return `${user.name} created ${entityType.toLowerCase()} "${details.title}"`;
case 'UPDATED':
case "UPDATED":
return `${user.name} updated ${entityType.toLowerCase()}`;
case 'DELETED':
case "DELETED":
return `${user.name} deleted ${entityType.toLowerCase()}`;
default:
return `${user.name} performed ${action}`;
@@ -427,7 +432,7 @@ try {
await activityService.logActivity(data);
} catch (error) {
// Log error but don't throw
logger.error('Failed to log activity', error);
logger.error("Failed to log activity", error);
}
```
@@ -454,6 +459,7 @@ Always use pagination for activity queries. Default limit is 50 items, maximum i
### Background Processing
For high-volume systems, consider:
- Async activity logging with message queues
- Batch inserts for multiple activities
- Separate read replicas for reporting