# SentryAgent.ai AgentIdP — Java SDK

Official Java client for the [SentryAgent.ai AgentIdP](https://sentryagent.ai) — an open-source Identity Provider for AI agents built on OAuth 2.0 (RFC 6749) and aligned with the [AGNTCY](https://agntcy.org) open standard.

## Requirements

- Java 17+
- A running AgentIdP server

## Installation

### Maven

```xml
<dependency>
  <groupId>ai.sentryagent</groupId>
  <artifactId>idp-sdk</artifactId>
  <version>1.0.0</version>
</dependency>
```

## Quick Start

```java
import ai.sentryagent.idp.AgentIdPClient;
import ai.sentryagent.idp.models.*;

AgentIdPClient client = new AgentIdPClient(
    "https://idp.example.com",
    "your-agent-client-id",
    "sk_live_..."
);

// Register a new AI agent
Agent agent = client.agents().registerAgent(
    RegisterAgentRequest.builder()
        .email("screener@example.com")
        .agentType("screener")
        .version("1.0.0")
        .capabilities(List.of("read", "classify"))
        .owner("platform-team")
        .deploymentEnv("production")
        .build()
);
System.out.println("Registered: " + agent.getAgentId());
```

## Authentication

OAuth 2.0 Client Credentials are managed automatically. Tokens are cached and refreshed 60 seconds before expiry. The `TokenManager` is thread-safe.

```java
// Custom scope (optional — defaults to all four scopes)
AgentIdPClient client = new AgentIdPClient(
    "https://idp.example.com",
    "my-client-id",
    "my-client-secret",
    "agents:read agents:write"
);
```

## Agent Registry

```java
// Register
Agent agent = client.agents().registerAgent(
    RegisterAgentRequest.builder()
        .email("...").agentType("screener").version("1.0.0")
        .capabilities(List.of("read")).owner("team").deploymentEnv("production")
        .build());

// List (with optional filters)
PaginatedAgents agents = client.agents().listAgents(
    ListAgentsParams.builder().status("active").page(1).limit(20).build());

// Get by ID
Agent agent = client.agents().getAgent("agent-uuid");

// Partial update
Agent updated = client.agents().updateAgent("agent-uuid",
    UpdateAgentRequest.builder().version("2.0.0").build());

// Decommission (permanent)
client.agents().decommissionAgent("agent-uuid");
```

## Credential Management

```java
// Generate (returns one-time ClientSecret)
CredentialWithSecret cred = client.credentials().generateCredential("agent-uuid");
System.out.println(cred.getClientSecret()); // store this — shown only once

// List
PaginatedCredentials creds = client.credentials().listCredentials("agent-uuid", 1, 20);

// Rotate
CredentialWithSecret newCred = client.credentials().rotateCredential("agent-uuid", "cred-uuid");

// Revoke
Credential revoked = client.credentials().revokeCredential("agent-uuid", "cred-uuid");
```

## Token Operations

```java
// Introspect (RFC 7662)
IntrospectResponse result = client.tokens().introspectToken("access-token-to-check");
if (result.isActive()) {
    System.out.println("Token belongs to: " + result.getSub());
}

// Revoke
client.tokens().revokeToken("access-token-to-revoke");
```

## Audit Log

```java
// Query with filters
PaginatedAuditEvents events = client.audit().queryAuditLog(
    QueryAuditParams.builder()
        .agentId("agent-uuid")
        .action("token.issued")
        .outcome("success")
        .fromDate("2026-01-01")
        .toDate("2026-01-31")
        .page(1).limit(50)
        .build());

// Get single event
AuditEvent event = client.audit().getAuditEvent("event-uuid");
```

## Async Methods

Every sync method has an async counterpart returning `CompletableFuture<T>`:

```java
CompletableFuture<Agent> future = client.agents().getAgentAsync("uuid-1");
future.thenAccept(agent -> System.out.println(agent.getAgentId()));

// Compose multiple async calls
client.agents().getAgentAsync("uuid-1")
    .thenCompose(agent -> client.credentials().generateCredentialAsync(agent.getAgentId()))
    .thenAccept(cred -> System.out.println("New secret: " + cred.getClientSecret()));
```

## Error Handling

All errors are thrown as `AgentIdPException` (extends `RuntimeException`):

```java
try {
    Agent agent = client.agents().getAgent("unknown-id");
} catch (AgentIdPException ex) {
    System.out.printf("code=%s status=%d%n", ex.getCode(), ex.getHttpStatus());
    // e.g. code=AgentNotFoundError status=404
}
```

| Method           | Type                     | Description                                     |
|------------------|--------------------------|-------------------------------------------------|
| `getCode()`      | `String`                 | Machine-readable error code                     |
| `getMessage()`   | `String`                 | Human-readable description                      |
| `getHttpStatus()`| `int`                    | HTTP status code (0 for network/build errors)   |
| `getDetails()`   | `Map<String, Object>`    | Optional structured context from the API        |

## API Coverage

| Endpoint                                         | Method | SDK Method                              |
|--------------------------------------------------|--------|-----------------------------------------|
| POST /api/v1/agents                              | POST   | `agents().registerAgent()`              |
| GET  /api/v1/agents                              | GET    | `agents().listAgents()`                 |
| GET  /api/v1/agents/:id                          | GET    | `agents().getAgent()`                   |
| PATCH /api/v1/agents/:id                         | PATCH  | `agents().updateAgent()`                |
| DELETE /api/v1/agents/:id                        | DELETE | `agents().decommissionAgent()`          |
| POST /api/v1/agents/:id/credentials              | POST   | `credentials().generateCredential()`   |
| GET  /api/v1/agents/:id/credentials              | GET    | `credentials().listCredentials()`      |
| POST /api/v1/agents/:id/credentials/:cid/rotate  | POST   | `credentials().rotateCredential()`     |
| DELETE /api/v1/agents/:id/credentials/:cid       | DELETE | `credentials().revokeCredential()`     |
| POST /api/v1/token                               | POST   | (TokenManager — automatic)              |
| POST /api/v1/token/introspect                    | POST   | `tokens().introspectToken()`           |
| POST /api/v1/token/revoke                        | POST   | `tokens().revokeToken()`               |
| GET  /api/v1/audit                               | GET    | `audit().queryAuditLog()`              |
| GET  /api/v1/audit/:id                           | GET    | `audit().getAuditEvent()`              |

## License

Apache 2.0 — see [LICENSE](../LICENSE).
