feat: Phase 1 MVP — complete AgentIdP implementation

Implements all P0 features per OpenSpec change phase-1-mvp-implementation:
- Agent Registry Service (CRUD) — full lifecycle management
- OAuth 2.0 Token Service (Client Credentials flow)
- Credential Management (generate, rotate, revoke)
- Immutable Audit Log Service

Tech: Node.js 18+, TypeScript 5.3+ strict, Express 4.18+, PostgreSQL 14+, Redis 7+
Standards: OpenAPI 3.0 specs, DRY/SOLID, zero `any` types
Quality: 18 unit test suites, 244 tests passing, 97%+ coverage
OpenAPI: 4 complete specs (14 endpoints total)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SentryAgent.ai Developer
2026-03-28 09:14:41 +00:00
parent 245f8df427
commit d3530285b9
78 changed files with 20590 additions and 1 deletions

View File

@@ -0,0 +1,86 @@
## ADDED Requirements
### Requirement: Register a new AI agent
The system SHALL create a new agent identity record with a system-assigned immutable UUID (`agentId`) when a valid `CreateAgentRequest` is received. The `email` field SHALL be unique across all agents. The agent SHALL be created with `status: active`. The system SHALL enforce a free-tier limit of 100 registered agents per account.
#### Scenario: Successful agent registration
- **WHEN** a POST request to `/agents` is received with a valid `CreateAgentRequest` body and a valid Bearer token
- **THEN** the system creates the agent, assigns a UUID `agentId`, sets `status` to `active`, sets `createdAt` and `updatedAt` to the current timestamp, and returns `201` with the full `Agent` object
#### Scenario: Duplicate email rejected
- **WHEN** a POST request to `/agents` is received with an `email` that is already registered
- **THEN** the system returns `409 Conflict` with `code: AGENT_ALREADY_EXISTS`
#### Scenario: Free tier limit enforced
- **WHEN** a POST request to `/agents` is received and the account already has 100 registered agents
- **THEN** the system returns `403 Forbidden` with `code: FREE_TIER_LIMIT_EXCEEDED` and `details.limit: 100`
#### Scenario: Invalid request body rejected
- **WHEN** a POST request to `/agents` is received with a missing required field or invalid field value (e.g. invalid semver, invalid email, invalid capability pattern)
- **THEN** the system returns `400 Bad Request` with `code: VALIDATION_ERROR` and `details` identifying the failing field
### Requirement: Retrieve a single agent by ID
The system SHALL return the full `Agent` record for a given `agentId`.
#### Scenario: Agent found
- **WHEN** a GET request to `/agents/{agentId}` is received with a valid Bearer token and a UUID that exists in the registry
- **THEN** the system returns `200 OK` with the full `Agent` object
#### Scenario: Agent not found
- **WHEN** a GET request to `/agents/{agentId}` is received with a UUID that does not exist
- **THEN** the system returns `404 Not Found` with `code: AGENT_NOT_FOUND`
### Requirement: List agents with pagination and filtering
The system SHALL return a paginated list of agents, orderd by `createdAt` descending, optionally filtered by `owner`, `agentType`, and/or `status`.
#### Scenario: Successful paginated list
- **WHEN** a GET request to `/agents` is received with optional `page`, `limit`, `owner`, `agentType`, `status` query parameters and a valid Bearer token
- **THEN** the system returns `200 OK` with a `PaginatedAgentsResponse` containing `data`, `total`, `page`, and `limit`
#### Scenario: Invalid pagination parameters rejected
- **WHEN** a GET request to `/agents` is received with `limit` greater than 100 or `page` less than 1
- **THEN** the system returns `400 Bad Request` with `code: VALIDATION_ERROR`
### Requirement: Update agent metadata
The system SHALL partially update a mutable agent record. `agentId`, `email`, and `createdAt` SHALL be immutable. Setting `status` to `decommissioned` SHALL be a one-way irreversible operation.
#### Scenario: Successful partial update
- **WHEN** a PATCH request to `/agents/{agentId}` is received with a valid partial `UpdateAgentRequest` body and a valid Bearer token
- **THEN** the system updates only the provided fields, sets `updatedAt` to the current timestamp, and returns `200 OK` with the full updated `Agent` object
#### Scenario: Attempt to modify immutable field rejected
- **WHEN** a PATCH request to `/agents/{agentId}` contains the `email` field
- **THEN** the system returns `400 Bad Request` with `code: IMMUTABLE_FIELD` and `details.field: email`
#### Scenario: Decommissioned agent cannot be updated
- **WHEN** a PATCH request to `/agents/{agentId}` targets an agent with `status: decommissioned`
- **THEN** the system returns `403 Forbidden` with `code: AGENT_DECOMMISSIONED`
### Requirement: Decommission (soft-delete) an agent
The system SHALL set an agent's `status` to `decommissioned` and revoke all of its active credentials. The agent record SHALL be retained for audit purposes. This operation SHALL be irreversible.
#### Scenario: Successful decommission
- **WHEN** a DELETE request to `/agents/{agentId}` is received with a valid Bearer token and the agent exists and is not already decommissioned
- **THEN** the system sets `status` to `decommissioned`, revokes all active credentials for this agent, and returns `204 No Content`
#### Scenario: Already decommissioned agent rejected
- **WHEN** a DELETE request to `/agents/{agentId}` is received for an agent that is already `decommissioned`
- **THEN** the system returns `409 Conflict` with `code: AGENT_ALREADY_DECOMMISSIONED`
### Requirement: Authentication required on all agent endpoints
All agent endpoints SHALL require a valid Bearer JWT in the `Authorization` header.
#### Scenario: Missing token rejected
- **WHEN** any request to `/agents` or `/agents/{agentId}` is received without an `Authorization: Bearer` header
- **THEN** the system returns `401 Unauthorized` with `code: UNAUTHORIZED`
#### Scenario: Invalid token rejected
- **WHEN** any request to `/agents` or `/agents/{agentId}` is received with an expired, malformed, or revoked Bearer token
- **THEN** the system returns `401 Unauthorized` with `code: UNAUTHORIZED`
### Requirement: Rate limiting on all agent endpoints
The system SHALL enforce a rate limit of 100 requests per minute per authenticated client. Rate limit state SHALL be tracked in Redis.
#### Scenario: Rate limit exceeded
- **WHEN** a client sends more than 100 requests to any agent endpoint within a 60-second window
- **THEN** the system returns `429 Too Many Requests` with `X-RateLimit-Limit`, `X-RateLimit-Remaining: 0`, and `X-RateLimit-Reset` headers