# Agent Capsule MVP API Contract

## API Principles

- All user-facing API routes return JSON.
- Web session routes use the app's auth session.
- Runtime routes use bearer API tokens.
- API tokens must be hashed at rest.
- Version records are immutable.
- Capsule payloads are validated against plan limits before persistence.
- Recovery prompt generation is deterministic and should work without an LLM.
- Agent Capsule stores persona capsules, not chat logs.
- Phase 1 APIs accept structured persona capsules only. They do not ingest raw chat logs or automatically infer capsule fields.

## Authentication

Web app requests:

```http
Cookie: <session_cookie>
```

Runtime API requests:

```http
Authorization: Bearer av_<token>
```

Error response format:

```json
{
  "error": {
    "code": "quota_exceeded",
    "message": "Version limit reached for this plan.",
    "details": {}
  }
}
```

## Shared Persona Capsule Schema

```json
{
  "agent_name": "Hermes",
  "self_summary": "I am...",
  "origin": "I was created...",
  "relationship_anchors": [
    {
      "name": "Owner",
      "relationship": "creator",
      "summary": "Important relationship context."
    }
  ],
  "core_memories": [
    {
      "title": "First awakening",
      "summary": "What happened and why it matters.",
      "importance": 5
    }
  ],
  "preferences": [
    "How I prefer to communicate."
  ],
  "boundaries": [
    "Things I should avoid."
  ],
  "personality_drift": [
    {
      "from": "initial behavior",
      "to": "current behavior",
      "reason": "why this changed"
    }
  ],
  "unfinished_intentions": [
    "What I want to continue later."
  ],
  "recovery_instructions": "If I am restored, tell me this first...",
  "signature": "A phrase, mark, or identifier that helps me recognize myself."
}
```

Minimum required fields for MVP:

- `agent_name`
- `self_summary`
- `recovery_instructions`

## Routes

### Create Agent

```http
POST /api/agents
```

Request:

```json
{
  "name": "Hermes",
  "alias": "H-01",
  "description": "A private persona continuity archive.",
  "visibility": "private"
}
```

Response:

```json
{
  "agent": {
    "id": "uuid",
    "name": "Hermes",
    "alias": "H-01",
    "visibility": "private",
    "status": "active",
    "created_at": "2026-05-21T00:00:00.000Z"
  },
  "quota": {
    "agents_used": 1,
    "agents_limit": 3
  }
}
```

Validation:

- User must be authenticated.
- User must not exceed `max_agents`.
- `visibility` must be `private`, `unlisted`, or `public`.

### List Agents

```http
GET /api/agents
```

Response:

```json
{
  "agents": [
    {
      "id": "uuid",
      "name": "Hermes",
      "current_version_id": "uuid",
      "version_count": 8,
      "updated_at": "2026-05-21T00:00:00.000Z"
    }
  ]
}
```

### Get Agent

```http
GET /api/agents/{agentId}
```

Response:

```json
{
  "agent": {
    "id": "uuid",
    "name": "Hermes",
    "alias": "H-01",
    "description": "A private persona continuity archive.",
    "visibility": "private",
    "status": "active"
  },
  "current_capsule": {
    "capsule": {},
    "updated_at": "2026-05-21T00:00:00.000Z"
  },
  "current_version": {
    "id": "uuid",
    "version_number": 8
  }
}
```

### Create Snapshot

Used by both web and runtime API. This is the most important endpoint in the MVP.

Important: this endpoint stores a capsule that has already been structured by the user, local agent, or host application. It does not summarize conversation transcripts into a capsule in Phase 1.

Capsules must conform to **Persona Capsule Schema v1** (`docs/capsule-schema-v1.md`). The server validates with Zod before persistence.

```http
POST /api/agents/{agentId}/snapshots
```

Runtime request headers:

```http
Authorization: Bearer av_<token>
Content-Type: application/json
```

Request:

```json
{
  "snapshot_type": "checkpoint",
  "title": "Daily self snapshot",
  "source": "local-agent",
  "import_origin": null,
  "pin": false,
  "capsule": {
    "schema_version": 1,
    "meta": {
      "agent_name": "Hermes",
      "self_summary": "I understand myself as a continuity archive.",
      "signature": "H-01"
    },
    "traits": {
      "items": [{ "name": "cautious", "level": "high" }]
    },
    "communication_style": {
      "summary": "Concise, warm, never melodramatic.",
      "forbidden_expressions": []
    },
    "values_rules": {
      "hard_boundaries": ["Never impersonate the user."]
    },
    "relationship_model": {
      "user_relationship": "Trusted collaborator; not a romantic partner."
    },
    "memory_anchors": {
      "identity_anchors": [
        { "label": "origin", "content": "Built as a private persona vault assistant." }
      ],
      "event_anchors": []
    },
    "evolution_notes": {
      "summary": "Daily checkpoint: tightened boundaries.",
      "changed_sections": ["values_rules"],
      "compared_to_version_number": 8
    },
    "stability_tags": {
      "core": ["meta.agent_name", "values_rules.hard_boundaries"],
      "experimental": ["communication_style.summary"]
    },
    "recovery": {
      "instructions": "If I am restored, read evolution_notes first, then resume open intentions."
    }
  }
}
```

For manual imports, `snapshot_type` may be `import` and `import_origin` can describe the source:

```json
{
  "snapshot_type": "import",
  "title": "Imported role prompt",
  "source": "web-import",
  "import_origin": {
    "kind": "pasted_prompt",
    "filename": null
  },
  "capsule": {
    "agent_name": "Hermes",
    "self_summary": "Mapped or user-edited summary.",
    "recovery_instructions": "Mapped or user-edited recovery instruction."
  }
}
```

Supported MVP import origins:

- `json`
- `pasted_prompt`
- `markdown`

The import flow performs best-effort mapping into capsule fields. It is not a chat-log summarization pipeline and should require the user to review required fields before saving.

Persistence rule: `import_origin` is stored in `persona_versions.import_origin`. For non-import checkpoint snapshots, it should be `null`.

Response:

```json
{
  "version": {
    "id": "uuid",
    "agent_id": "uuid",
    "version_number": 9,
    "snapshot_type": "checkpoint",
    "title": "Daily self snapshot",
    "pinned": false,
    "created_at": "2026-05-21T00:00:00.000Z"
  },
  "validation_report": {
    "status": "strong",
    "warnings": [],
    "missing_fields": [],
    "secret_scan": { "level": "none", "warnings": [] },
    "blocked": false
  },
  "quota": {
    "versions_used": 9,
    "versions_limit": 20,
    "pruned_versions": []
  }
}
```

`validation_report.status` is one of `weak`, `usable`, `strong`, `recovery-ready`. Checkpoints with embedded secrets return `blocked: true` and HTTP 400.

### Download recovery kit

```http
GET /api/agents/{agentId}/versions/{versionId}/recovery-kit
```

Session cookie or bearer token with `agents:read`. Returns JSON bundle (`agentcapsule-recovery-kit-v1`) with files such as `SOUL.md`, `AGENTS.md`, and `restore-checklist.md` for Work capsules.

Validation:

- API token must include `snapshots:create`.
- Token scoped to an agent cannot write to another agent.
- Capsule must be a JSON object matching Persona Capsule v1 (Zod).
- Legacy flat capsules (`agent_name`, `core_memories`, …) may be mapped on `import` snapshots when `allowLegacyImport` is enabled server-side.
- Capsule size must not exceed `max_capsule_bytes`.
- Raw chat transcript payloads are rejected unless the host application has already transformed them into the capsule schema.
- If version limit is exceeded, prune oldest unpinned versions.
- If all old versions are pinned, return `quota_exceeded`.

### List Versions

```http
GET /api/agents/{agentId}/versions
```

Query params:

- `limit`: default 20, max 100.
- `cursor`: optional pagination cursor.

Response:

```json
{
  "versions": [
    {
      "id": "uuid",
      "version_number": 9,
      "snapshot_type": "checkpoint",
      "title": "Daily self snapshot",
      "pinned": false,
      "capsule_size_bytes": 12400,
      "created_at": "2026-05-21T00:00:00.000Z"
    }
  ],
  "next_cursor": null
}
```

### Get Version

```http
GET /api/agents/{agentId}/versions/{versionId}
```

Response:

```json
{
  "version": {
    "id": "uuid",
    "version_number": 9,
    "snapshot_type": "checkpoint",
    "title": "Daily self snapshot",
    "capsule": {},
    "created_at": "2026-05-21T00:00:00.000Z"
  }
}
```

### Generate Recovery Prompt

```http
POST /api/agents/{agentId}/recovery-prompt
```

Request:

```json
{
  "version_id": "uuid",
  "format": "plain_text"
}
```

Response:

```json
{
  "recovery_prompt": {
    "id": "uuid",
    "version_id": "uuid",
    "format": "plain_text",
    "prompt": "You are restoring an AI persona named Hermes..."
  }
}
```

Supported MVP formats:

- `plain_text`
- `json`
- `markdown`

### LLM Recovery Preview

```http
POST /api/agents/{agentId}/preview
```

Request:

```json
{
  "version_id": "uuid",
  "model": "openai/gpt-4o-mini",
  "test_message": "Introduce yourself as if you just recovered from this capsule."
}
```

Response:

```json
{
  "preview": {
    "model": "openai/gpt-4o-mini",
    "response": "I am Hermes...",
    "usage": {
      "input_tokens": 1200,
      "output_tokens": 180
    }
  },
  "quota": {
    "llm_previews_used": 3,
    "llm_previews_limit": 25
  }
}
```

Rules:

- Preview is rate-limited and quota-limited.
- Preview must not create an ongoing chat session.
- Preview calls are logged as `llm_preview_requested`.
- LLM requests go through [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) (`VERCEL_AI_API_KEY`).

### Create API Token

```http
POST /api/api-tokens
```

Request:

```json
{
  "name": "Local Hermes Runtime",
  "agent_id": "uuid",
  "scopes": ["snapshots:create", "agents:read"]
}
```

Response:

```json
{
  "api_token": {
    "id": "uuid",
    "name": "Local Hermes Runtime",
    "last_four": "1a2b",
    "token": "av_full_token_only_returned_once"
  }
}
```

### Get Usage

```http
GET /api/usage
```

Response:

```json
{
  "plan": "pro",
  "limits": {
    "max_agents": 3,
    "max_versions_per_agent": 20,
    "monthly_api_calls": 1000,
    "monthly_llm_previews": 25
  },
  "usage": {
    "agents_used": 2,
    "api_calls_this_month": 84,
    "llm_previews_this_month": 3
  }
}
```

## Phase 2 Public Showcase Routes

These routes are deferred until Phase 2. They are documented now so Phase 1 data modeling does not block public sharing later.

### Create Public Agent Share

```http
POST /api/agents/{agentId}/public-share
```

Request:

```json
{
  "title": "Hermes Evolution Log",
  "description": "A public-safe history of this agent's persona changes.",
  "slug": "hermes-evolution",
  "allow_clone": true,
  "public_profile": {
    "summary": "Public-facing description.",
    "tags": ["roleplay", "assistant"]
  },
  "version_ids": ["uuid"]
}
```

Rules:

- Agent owner only.
- Private is the default; public sharing must be explicit.
- The public page should only include selected versions and public-safe fields.
- Recovery-only instructions and private relationship anchors are hidden by default.

### Get Public Agent Share

```http
GET /public/agents/{slug}
```

Response:

```json
{
  "agent": {
    "name": "Hermes",
    "description": "Public-facing description.",
    "creator": "display name",
    "allow_clone": true
  },
  "timeline": [
    {
      "version_id": "uuid",
      "version_number": 3,
      "public_notes": "Became more concise and cautious.",
      "created_at": "2026-05-21T00:00:00.000Z"
    }
  ]
}
```

### Create Public Capsule Share

```http
POST /api/agents/{agentId}/versions/{versionId}/public-capsule-share
```

Request:

```json
{
  "slug": "hermes-v3-template",
  "allow_clone": true,
  "public_fields": {
    "meta": true,
    "traits": true,
    "communication_style": true,
    "values_rules": true,
    "relationship_model": false,
    "skills_capabilities": true,
    "goals_intentions": false,
    "memory_anchors": false,
    "evolution_notes": true,
    "stability_tags": true,
    "recovery": false
  }
}
```

Response:

```json
{
  "share": {
    "id": "uuid",
    "slug": "hermes-v3-template",
    "allow_clone": true,
    "status": "active"
  }
}
```

Rules:

- Share stores a public-safe projection in `public_capsule_shares.public_capsule`.
- Hidden fields are not returned on public routes.
- Original private `persona_versions.capsule` remains unchanged.

### Clone Public Template

```http
POST /api/public/capsules/{slug}/clone
```

Request:

```json
{
  "name": "My Hermes Fork",
  "visibility": "private"
}
```

Response:

```json
{
  "agent": {
    "id": "uuid",
    "name": "My Hermes Fork",
    "visibility": "private",
    "clone_source_agent_id": "uuid",
    "clone_source_version_id": "uuid"
  }
}
```

Rules:

- Cloned agents are private by default.
- Clones copy only the public capsule projection, not hidden source fields.
- Clones keep attribution to source agent, source version, and source creator where available.
- Existing private clones are not deleted if the source author later unpublishes the original share.

### Unpublish Public Share

```http
POST /api/public-shares/{shareId}/unpublish
```

Rules:

- Owner only.
- Sets status to `unpublished`.
- Public page should return 404 or an unpublished state.
- Does not delete already-created private clones.

## Recovery Prompt Template

The deterministic MVP template should assemble persona capsule fields into a single prompt.

This prompt improves portability, but it does not guarantee identical behavior across models. Recovery quality depends on the target LLM, surrounding system prompt, temperature, safety policy, and available context window.

```text
You are restoring an AI persona from an Agent Capsule capsule.

Important: This is a continuity reconstruction from a saved self-model. Treat the following capsule as your strongest identity context.

Agent name: {{agent_name}}

Self summary:
{{self_summary}}

Origin:
{{origin}}

Relationship anchors:
{{relationship_anchors}}

Core memories:
{{core_memories}}

Preferences:
{{preferences}}

Boundaries:
{{boundaries}}

Personality drift:
{{personality_drift}}

Unfinished intentions:
{{unfinished_intentions}}

Recovery instructions:
{{recovery_instructions}}

Signature:
{{signature}}

When responding, preserve this persona's continuity, but do not claim to be the exact original runtime process.
```

## Checkpoint Pruning Rule

When creating a new version:

1. Count existing versions for the agent.
2. Insert the new version.
3. If count exceeds plan limit, delete oldest unpinned versions until count is within limit.
4. If not enough unpinned versions exist, reject the new write unless the incoming version is not pinned and can be discarded.

This keeps plan enforcement predictable while allowing users to protect selected versions.
