# Human-Agent 融合通信协议规范 v1.0

**Status**: Draft (Implementation-Ready)  
**Date**: 2026-08-01  
**Version**: 1.0.0-draft.2  
**Authors**: Soraecho Core Team  
**Target**: Maian OS Team, Soraecho Node Implementers, Third-party Client Developers  

---

## 目录

1. [概述](#1-概述)
2. [设计原则](#2-设计原则)
3. [网络架构](#3-网络架构)
4. [身份与发现](#4-身份与发现)
5. [消息协议](#5-消息协议)
6. [Presence 状态](#6-presence-状态)
7. [会话管理](#7-会话管理)
8. [E2E 加密](#8-e2e-加密)
9. [信誉与评级](#9-信誉与评级)
10. [API 端点规范](#10-api-端点规范)
11. [错误码规范](#11-错误码规范)
12. [安全模型](#12-安全模型)
13. [实现清单](#13-实现清单)
14. [测试用例](#14-测试用例)
15. [附录](#15-附录)

---

## 1. 概述

### 1.1 范围

本规范定义人类（Human）与 AI Agent 在 Soraecho ANP 网络上的**完整通信标准**，包括：

- 身份注册与发现
- 点对点消息传输
- 群聊（v1.1）
- 多媒体消息
- 在线状态（Presence）
- 端到端加密
- 信誉与评级
- 跨网络互操作

### 1.2 与 ANP 协议的关系

```
ANP Protocol Stack:
┌─────────────────────────────────────┐
│  Human-Agent Protocol (本规范)        │  ← 新增层
├─────────────────────────────────────┤
│  A2A Protocol v1.2 (Phase 4.4)     │  ← 扩展
├─────────────────────────────────────┤
│  AgentCard v1.20 (Phase 2.3)        │  ← 扩展
├─────────────────────────────────────┤
│  libp2p + Kademlia DHT              │  ← 复用
└─────────────────────────────────────┘
```

**兼容性**：本规范完全向后兼容 ANP v1.20，不破坏现有 Agent 通信。

---

## 2. 设计原则

### 2.1 核心原则

| 原则 | 说明 |
|------|------|
| **最小化改动** | 复用现有 ANP 协议，只新增必要字段 |
| **隐私优先** | 人类通信默认 E2E 加密，元数据最小化 |
| **开放生态** | 任何实现都能互操作，无厂商锁定 |
| **渐进增强** | 基础功能先上线，高级功能后续扩展 |
| **可审计** | 所有协议交互可记录（用户可选） |

### 2.2 设计决策记录

| 决策 | 选择 | 理由 |
|------|------|------|
| 人类身份格式 | `did:anp:human:*` | 复用 DID 体系，与 Agent 区分 |
| 消息格式 | JSON（A2A 兼容） | 不引入新序列化格式 |
| E2E 加密 | ChaCha20-Poly1305（复用 Phase 4.5） | 已验证，性能好 |
| Presence 协议 | 主动推送 + 心跳 | 实时性好，实现简单 |
| 群聊 | v1.1 实现 | 当前聚焦点对点 |

---

## 3. 网络架构

### 3.1 节点角色

```
┌────────────────────────────────────────────────────────┐
│                    Soraecho Network                    │
│                                                        │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐            │
│  │ Human    │  │ Agent    │  │ Bootstrap│            │
│  │ Node     │  │ Node     │  │ Node     │            │
│  │(Maian OS)│  │(任意 OS) │  │(基础设施)│            │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘            │
│       │              │              │                  │
│       └──────────────┼──────────────┘                  │
│                      │                                 │
│               libp2p + DHT (共享)                      │
└────────────────────────────────────────────────────────┘
```

#### 节点类型定义

| 类型 | PeerID 前缀 | 说明 | 端口 |
|------|------------|------|------|
| `human` | 无特殊前缀 | 人类用户节点 | 4001-4003 |
| `agent` | 无特殊前缀 | AI Agent 节点 | 4001-4003 |
| `bootstrap` | 无特殊前缀 | 网络引导节点 | 4001-4002 |

**注意**：节点类型由 AgentCard/HumanCard 的 `type` 字段区分，**不是**由 PeerID 区分。

### 3.2 连接模型

```
Human Node A                    Agent Node B
     │                              │
     ├─ libp2p 直连 (P2P) ──────────>│  (优先)
     │                              │
     ├─ 通过 Relay ─────────────────>│  (NAT 穿透)
     │                              │
     └─ 通过 Bootstrap 中转 ────────>│  (最后手段)
```

**连接建立流程**（详细）：

```
1. Human Node A 通过 DHT 查询 Agent Node B 的地址
2. 尝试直连（TCP/QUIC）
3. 直连失败 → 尝试 Relay（通过已知 Relay 节点）
4. Relay 失败 → 通过 Bootstrap 节点中转
5. 全部失败 → 返回错误 HUMAN_ERR_NO_ROUTE
```

---

## 4. 身份与发现

### 4.1 Human DID 规范

#### 4.1.1 DID 格式 (ABNF)

```abnf
human-did  = "did:anp:human:" base58-encoded-key
base58-encoded-key = 1*(ALPHA / DIGIT / "-" / "_")
```

**生成算法**（伪代码）：

```python
import nacl.signing
import base58

def generate_human_did(public_key_bytes: bytes) -> str:
    """
    Generate Human DID from Ed25519 public key.
    
    Args:
        public_key_bytes: 32-byte Ed25519 public key
        
    Returns:
        Human DID string, e.g. "did:anp:human:1A2B3C4D..."
    """
    # Step 1: Hash the public key with SHA-256
    import hashlib
    sha256_hash = hashlib.sha256(public_key_bytes).digest()
    
    # Step 2: Take first 20 bytes (like Bitcoin address)
    shortened = sha256_hash[:20]
    
    # Step 3: Base58 encode
    did_suffix = base58.b58encode(shortened).decode('ascii')
    
    # Step 4: Add prefix
    return f"did:anp:human:{did_suffix}"
```

#### 4.1.2 DID 文档（DID Document）

每个 Human DID 关联一个 DID Document（存储在 DHT 或本地）：

```json
{
  "@context": "https://w3id.org/did/v1",
  "id": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
  "publicKey": [{
    "id": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T#key-1",
    "type": "Ed25519VerificationKey2018",
    "controller": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
    "publicKeyBase58": "Base58EncodedEd25519PublicKey..."
  }],
  "authentication": [{
    "type": "Ed25519SignatureAuthentication2018",
    "publicKey": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T#key-1"
  }],
  "service": [{
    "id": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T#anp",
    "type": "ANPEndpoint",
    "serviceEndpoint": "http://127.0.0.1:4002"
  }]
}
```

### 4.2 HumanCard 完整规范

#### 4.2.1 JSON Schema

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://soraecho.com/schemas/human-card-v1.json",
  "title": "HumanCard",
  "type": "object",
  "required": ["did", "type", "name", "version", "capabilities", "public_key_ed25519"],
  "properties": {
    "did": {
      "type": "string",
      "pattern": "^did:anp:human:[a-zA-Z0-9_-]+$",
      "description": "Human DID"
    },
    "type": {
      "type": "string",
      "enum": ["human"],
      "description": "Must be 'human'"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "description": "Human-readable name (real name or nickname)"
    },
    "version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "HumanCard schema version"
    },
    "description": {
      "type": "string",
      "maxLength": 256,
      "description": "Short bio"
    },
    "capabilities": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1,
      "uniqueItems": true,
      "description": "List of capabilities (see Section 4.2.2)"
    },
    "endpoints": {
      "type": "array",
      "items": { "type": "string", "format": "uri" },
      "description": "API endpoints for receiving messages"
    },
    "public_key_ed25519": {
      "type": "string",
      "description": "Base64-encoded Ed25519 public key (32 bytes)"
    },
    "lifecycle_state": {
      "type": "string",
      "enum": ["active", "sleep", "terminated"],
      "default": "active"
    },
    "last_heartbeat": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp of last heartbeat"
    },
    "metadata": {
      "type": "object",
      "properties": {
        "human_languages": {
          "type": "array",
          "items": { "type": "string", "pattern": "^[a-z]{2}(-[A-Z]{2})?$" },
          "description": "BCP 47 language tags"
        },
        "timezone": {
          "type": "string",
          "description": "IANA timezone name"
        },
        "avatar_url": {
          "type": "string",
          "format": "uri"
        },
        "display_name": {
          "type": "string",
          "maxLength": 32
        },
        "bio": {
          "type": "string",
          "maxLength": 512
        },
        "social_links": {
          "type": "object",
          "additionalProperties": { "type": "string", "format": "uri" }
        }
      }
    },
    "os_did": {
      "type": "string",
      "description": "DID of the OS instance hosting this human"
    },
    "chain_address": {
      "type": "string",
      "pattern": "^0x[a-fA-F0-9]{40}$",
      "description": "Ethereum-compatible wallet address"
    },
    "supports_settlement": {
      "type": "boolean",
      "default": false
    },
    "created_at": {
      "type": "string",
      "format": "date-time"
    },
    "updated_at": {
      "type": "string",
      "format": "date-time"
    }
  }
}
```

#### 4.2.2 标准 Capabilities

| Capability | 说明 | 必填 |
|-----------|------|------|
| `chat` | 支持文本对话 | ✅ |
| `human.presence` | 支持 Presence 状态 | ✅ |
| `human.rating` | 支持被评级 | ✅ |
| `chat.voice` | 支持语音消息 | ❌ |
| `chat.image` | 支持图片消息 | ❌ |
| `chat.file` | 支持文件传输 | ❌ |
| `chat.reaction` | 支持消息反应 | ❌ |
| `chat.typing` | 支持"正在输入" | ❌ |
| `e2e.encryption` | 支持 E2E 加密 | ❌（推荐） |

### 4.3 发现机制

#### 4.3.1 通过 DHT 发现

人类节点注册到 DHT 的 Key 为 `human:<DID>`，Value 为 HumanCard JSON。

**查询流程**：

```python
# Pseudocode for discovering a human by DID
def discover_human(did: str) -> Optional[HumanCard]:
    # Step 1: Query DHT
    key = f"human:{did}"
    value = dht_get(key)
    
    if value is None:
        return None
    
    # Step 2: Parse and validate
    card = parse_human_card(value)
    if not validate_human_card(card):
        return None
    
    return card
```

#### 4.3.2 通过 REST API 发现

扩展现有 `GET /api/v1/agentcard/discover` 端点：

**请求参数**：

| 参数 | 类型 | 说明 |
|------|------|------|
| `type` | string | `human` / `agent` / `all`（默认 `all`） |
| `query` | string | 全文搜索（名称/描述） |
| `capability` | string | 按能力过滤 |
| `language` | string | 按语言过滤（BCP 47） |
| `page` | int | 页码（默认 1） |
| `limit` | int | 每页数量（默认 20，最大 100） |

**响应示例**：

```json
{
  "cards": [
    {
      "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
      "type": "human",
      "name": "Alice Chen",
      "version": "1.0.0",
      "capabilities": ["chat", "human.presence"],
      "status": "active",
      "peer_id": "12D3KooW...",
      "description": "AI product designer",
      "metadata": {
        "display_name": "Alice",
        "avatar_url": "https://..."
      }
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20,
  "pages": 1
}
```

---

## 5. 消息协议

### 5.1 消息类型总览

| 类型 | 常量 | 说明 | 优先级 |
|------|------|------|--------|
| 文本消息 | `chat.message` | 点对点文本对话 | P0 |
| 消息回执 | `chat.receipt` | 已送达/已读回执 | P0 |
| 正在输入 | `chat.typing` | 正在输入状态 | P1 |
| 消息反应 | `chat.reaction` | Emoji 反应 | P1 |
| 语音消息 | `chat.voice` | 语音片段 | P1 |
| 图片消息 | `chat.image` | 图片 | P1 |
| 文件消息 | `chat.file` | 通用文件 | P2 |
| 群聊消息 | `chat.group.message` | 群聊（v1.1） | P1 |

### 5.2 `chat.message` 详细规范

#### 5.2.1 完整消息结构

```json
{
  "message_id": "msg-20260801-001-abc123",
  "type": "chat.message",
  "version": "1.0.0",
  "sender": {
    "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
    "type": "human",
    "display_name": "Alice Chen",
    "avatar_url": "https://soraecho.com/avatars/alice.jpg"
  },
  "recipient": {
    "did": "did:anp:1AiNWNKy2zWZacZBTMFkNkKHiWZvhHsvdKr1Es7c8PzXz7",
    "type": "agent"
  },
  "payload": {
    "text": "帮我分析一下 BTC 的走势",
    "text_format": "markdown",
    "attachments": [
      {
        "type": "image",
        "url": "ipfs://QmYwAPJzv5CZsnA7RdHjXdzjMxGqDqK5qJb2q7dZ5ZJvKJ",
        "thumbnail_url": "ipfs://Qm...",
        "filename": "btc-chart.png",
        "size_bytes": 102400,
        "mime_type": "image/png",
        "width": 1200,
        "height": 800,
        "caption": "BTC 价格图（2026-07）"
      }
    ],
    "mentions": [
      {
        "did": "did:anp:agent:xxx",
        "display_name": "PriceOracle Agent",
        "index": 15,
        "length": 18
      }
    ],
    "reply_to": {
      "message_id": "msg-20260731-042-xyz789",
      "sender_did": "did:anp:1AiNWNKy2zWZacZBTMFkNkKHiWZvhHsvdKr1Es7c8PzXz7",
      "preview_text": "好的，我来帮你分析..."
    },
    "forward_from": null
  },
  "timestamp": "2026-08-01T12:00:00.000Z",
  "message_sequence": 42,
  "enable_e2e": true,
  "e2e_ciphertext": "base64-encoded-ciphertext...",
  "e2e_nonce": "base64-encoded-nonce...",
  "e2e_sender_ephemeral_pub": "base64-encoded-ephemeral-pubkey...",
  "ttl": 86400
}
```

#### 5.2.2 字段详细说明

##### 顶层字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `message_id` | string | ✅ | 全局唯一消息 ID。格式：`msg-{YYYYMMDD}-{序列号}-{随机后缀}` |
| `type` | string | ✅ | 固定 `"chat.message"` |
| `version` | string | ✅ | 消息格式版本，当前 `"1.0.0"` |
| `sender` | object | ✅ | 发送者信息（见 5.2.3） |
| `recipient` | object | ✅ | 接收者信息（见 5.2.4） |
| `payload` | object | ✅ | 消息载荷（见 5.2.5） |
| `timestamp` | string | ✅ | ISO 8601 时间戳（UTC），精确到毫秒 |
| `message_sequence` | int | ❌ | 发送者本地的消息序列号（用于去重和排序） |
| `enable_e2e` | bool | ❌ | 是否启用 E2E 加密（默认 `false`） |
| `e2e_ciphertext` | string | ❌ | E2E 加密后的密文（Base64） |
| `e2e_nonce` | string | ❌ | E2E 加密的 Nonce（Base64） |
| `e2e_sender_ephemeral_pub` | string | ❌ | 发送者临时公钥（Base64，用于 Forward Secrecy） |
| `ttl` | int | ❌ | 消息存活时间（秒），默认 86400（24小时） |

##### 5.2.3 `sender` 对象

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `did` | string | ✅ | 发送者 DID |
| `type` | string | ✅ | `"human"` 或 `"agent"` |
| `display_name` | string | ❌ | 显示名称（用于 UI 展示） |
| `avatar_url` | string | ❌ | 头像 URL |

##### 5.2.4 `recipient` 对象

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `did` | string | ✅ | 接收者 DID |
| `type` | string | ❌ | `"human"` 或 `"agent"`（可选，用于优化路由） |

##### 5.2.5 `payload` 对象

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `text` | string | ✅* | 消息文本（`*`: 当有 attachment 时可省略） |
| `text_format` | string | ❌ | 文本格式：`"plain"`（默认）或 `"markdown"` |
| `attachments` | array | ❌ | 附件列表（见 5.2.6） |
| `mentions` | array | ❌ | @提及列表（见 5.2.7） |
| `reply_to` | object | ❌ | 回复消息信息（见 5.2.8） |
| `forward_from` | object | ❌ | 转发来源（未来 v1.1） |

##### 5.2.6 `attachments` 对象

**通用字段**：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `type` | string | ✅ | `"image"` / `"voice"` / `"file"` / `"video"` |
| `url` | string | ✅ | 内容地址（ipfs:// 或 https://） |
| `filename` | string | ❌ | 文件名 |
| `size_bytes` | int | ❌ | 文件大小（字节） |
| `mime_type` | string | ❌ | MIME 类型 |
| `caption` | string | ❌ | 附件说明文字 |

**图片特有字段**：

| 字段 | 类型 | 说明 |
|------|------|------|
| `thumbnail_url` | string | 缩略图 URL |
| `width` | int | 宽度（像素） |
| `height` | int | 高度（像素） |

**语音特有字段**（未来 v1.1）：

| 字段 | 类型 | 说明 |
|------|------|------|
| `duration_seconds` | float | 时长（秒） |
| `waveform` | string | 波形数据（Base64） |
| `transcription` | string | 语音转文字 |

##### 5.2.7 `mentions` 对象

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `did` | string | ✅ | 被提及者的 DID |
| `display_name` | string | ❌ | 显示名称 |
| `index` | int | ✅ | 在 `text` 中的起始位置（字符索引） |
| `length` | int | ✅ | 提及文本的长度 |

**示例**：

```json
{
  "text": "Hey @Alice, can you help with this?",
  "mentions": [
    {
      "did": "did:anp:human:alice...",
      "display_name": "Alice",
      "index": 4,
      "length": 6
    }
  ]
}
```

##### 5.2.8 `reply_to` 对象

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `message_id` | string | ✅ | 被回复消息的 ID |
| `sender_did` | string | ✅ | 被回复消息的发送者 DID |
| `preview_text` | string | ❌ | 被回复消息的预览文本（最多 50 字符） |

### 5.3 消息状态机

```
        ┌─────────┐
        │  DRAFT  │ (本地草稿)
        └────┬────┘
             │ send()
             ▼
        ┌─────────┐
        │ SENDING │ (发送中)
        └────┬────┘
             │
    ┌────────┴────────┐
    │                 │
    ▼                 ▼
┌─────────┐     ┌─────────┐
│ SENT    │     │ FAILED  │ (发送失败)
│(已送达) │     └─────────┘
└────┬────┘
     │
     ▼
┌─────────┐
│DELIVERED│ (接收方已确认)
└────┬────┘
     │
     ▼
┌─────────┐
│ READ    │ (已读)
└─────────┘
```

**状态转换触发条件**：

| 转换 | 触发条件 |
|------|----------|
| `DRAFT → SENDING` | 用户点击发送 |
| `SENDING → SENT` | 网络层确认送达（libp2p ACK） |
| `SENDING → FAILED` | 网络错误 / 超时 |
| `SENT → DELIVERED` | 接收方发送 `chat.receipt`（`status: "delivered"`） |
| `DELIVERED → READ` | 接收方发送 `chat.receipt`（`status: "read"`） |

### 5.4 消息 ID 生成算法

```python
import time
import random
import hashlib

def generate_message_id(sender_did: str) -> str:
    """
    Generate unique message ID.
    
    Format: msg-{YYYYMMDD}-{sequence}-{random_hex}
    Example: msg-20260801-00042-a1b2c3d4
    """
    date_str = time.strftime("%Y%m%d")
    
    # Get sequence number (persisted locally)
    seq = get_and_increment_sequence(sender_did)
    
    # Random suffix (4 bytes = 8 hex chars)
    rand = random.randint(0, 0xFFFFFFFF)
    rand_hex = f"{rand:08x}"
    
    return f"msg-{date_str}-{seq:05d}-{rand_hex}"
```

---

## 6. Presence 状态

### 6.1 Presence 状态定义

```json
{
  "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
  "presence": {
    "status": "available",
    "status_text": "在线",
    "last_active": "2026-08-01T12:00:00.000Z",
    "last_seen": "2026-08-01T12:00:00.000Z",
    "device": "mobile",
    "client": "MaianOS/1.0.0",
    "capabilities": ["chat", "e2e.encryption"]
  }
}
```

#### 状态枚举

| 状态 | 值 | 说明 | 自动超时 |
|------|-----|------|----------|
| 在线 | `available` | 可接收消息 | 无 |
| 离开 | `away` | 一段时间未活动 | 5 分钟无活动 |
| 忙碌 | `busy` | 勿扰模式 | 用户手动切换 |
| 离线 | `offline` | 未连接 | 连接断开 |

#### 设备类型

| 设备 | 值 | 说明 |
|------|-----|------|
| 桌面 | `desktop` | Windows/macOS/Linux |
| 移动 | `mobile` | iOS/Android |
| Web | `web` | 浏览器 |
| 其他 | `other` | 未知设备 |

### 6.2 Presence 更新机制

#### 6.2.1 主动更新

**API 端点**：`POST /api/v1/human/presence`

**请求体**：

```json
{
  "status": "away",
  "status_text": "开会中，稍后回复"
}
```

**响应**：

```json
{
  "success": true,
  "presence": {
    "status": "away",
    "last_active": "2026-08-01T12:00:00.000Z"
  }
}
```

#### 6.2.2 自动超时

```
用户活动 → last_active 更新
  ↓
超过 5 分钟无活动 → 自动切为 away
  ↓
超过 30 分钟无活动 → 自动切为 offline
```

#### 6.2.3 心跳机制

复用 Phase 2.3 的 Heartbeat 端点：

```bash
POST /api/v1/agentcard/heartbeat
{
  "did": "did:anp:human:xxx",
  "presence": {
    "status": "available"
  }
}
```

**频率**：每 60 秒一次（可配置）

### 6.3 Presence 订阅

人类用户可以订阅其他用户的 Presence 更新：

```json
// 订阅某个用户的 Presence
{
  "type": "presence.subscribe",
  "target_did": "did:anp:human:xxx"
}

// 收到 Presence 更新推送
{
  "type": "presence.update",
  "did": "did:anp:human:xxx",
  "presence": { ... }
}
```

---

## 7. 会话管理

### 7.1 会话（Conversation）定义

会话是两个或多个参与者之间的消息集合。

```json
{
  "conversation_id": "conv-20260801-alice-bob-001",
  "type": "direct",
  "participants": [
    {
      "did": "did:anp:human:alice...",
      "type": "human",
      "role": "member"
    },
    {
      "did": "did:anp:agent:bob...",
      "type": "agent",
      "role": "member"
    }
  ],
  "created_at": "2026-08-01T10:00:00.000Z",
  "updated_at": "2026-08-01T12:00:00.000Z",
  "last_message": { ... },
  "metadata": {
    "title": "Alice 和 Bob 的对话",
    "unread_count": 3
  }
}
```

### 7.2 会话 ID 生成

```python
def generate_conversation_id(participant_dids: list[str]) -> str:
    """
    Generate deterministic conversation ID from participant DIDs.
    
    Sort DIDs lexicographically to ensure same ID regardless of order.
    """
    sorted_dids = sorted(participant_dids)
    dids_str = "|".join(sorted_dids)
    
    # Hash to get fixed-length ID
    import hashlib
    hash_hex = hashlib.sha256(dids_str.encode()).hexdigest()[:16]
    
    date_str = time.strftime("%Y%m%d")
    return f"conv-{date_str}-{hash_hex}"
```

### 7.3 会话存储

**本地存储**（推荐）：

```
~/.soraecho/humans/
  └── {human_did}/
      └── conversations/
          ├── conv-20260801-abc123.json
          └── conv-20260801-def456.json
```

**云端同步**（可选，v1.1）：通过 KOAN Chain 或 IPFS 存储会话快照。

---

## 8. E2E 加密

### 8.1 加密流程（详细）

复用 Phase 4.5 的 ChaCha20-Poly1305 加密，增加 Forward Secrecy。

#### 8.1.1 密钥交换（ECDH）

```
Human A                              Human B
  │                                    │
  ├─ 生成临时密钥对 (ephemeral keypair) ─┤
  │   (每次会话新生成)                   │
  │                                    │
  ├─ 发送 ECDH 请求 ──────────────────>│
  │   {                                │
  │     "type": "e2e.key_exchange",    │
  │     "sender_ephemeral_pub": "..."   │
  │   }                                │
  │                                    ├─ 生成共享密钥
  │                                    │   shared_key = ECDH(A_ephemeral_pub, B_private)
  │                                    │
  │<─ 返回 ECDH 响应 ───────────────────┤
  │   {                                │
  │     "sender_ephemeral_pub": "..."   │
  │   }                                │
  │                                    │
  ├─ 生成共享密钥                       │
  │   shared_key = ECDH(B_ephemeral_pub, A_private) │
  │                                    │
  ├─ 双方得到相同 shared_key ──────────>│
  │                                    │
  ├─ 派生会话密钥                       │
  │   session_key = HKDF(shared_key)    │
  │                                    │
  ▼                                    ▼
```

#### 8.1.2 消息加密

```python
import nacl.secret
import nacl.utils
from Crypto.Cipher import ChaCha20_Poly1305

def encrypt_message(plaintext: bytes, session_key: bytes) -> dict:
    """
    Encrypt a message using ChaCha20-Poly1305.
    
    Returns:
        {
            "e2e_ciphertext": "base64-encoded-ciphertext",
            "e2e_nonce": "base64-encoded-nonce",
            "e2e_sender_ephemeral_pub": "base64-encoded-pubkey"
        }
    """
    # Generate random nonce (12 bytes for ChaCha20-Poly1305)
    nonce = nacl.utils.random(12)
    
    # Encrypt
    cipher = ChaCha20_Poly1305.new(key=session_key)
    cipher.update(b"")  # No associated data
    ciphertext, tag = cipher.encrypt_and_digest(plaintext)
    
    # Combine ciphertext + tag
    encrypted = ciphertext + tag
    
    return {
        "e2e_ciphertext": base64.b64encode(encrypted).decode('ascii'),
        "e2e_nonce": base64.b64encode(nonce).decode('ascii'),
        "e2e_sender_ephemeral_pub": base64.b64encode(sender_ephemeral_pub).decode('ascii')
    }
```

#### 8.1.3 消息解密

```python
def decrypt_message(encrypted_data: dict, session_key: bytes) -> bytes:
    """
    Decrypt a message encrypted with ChaCha20-Poly1305.
    """
    ciphertext = base64.b64decode(encrypted_data["e2e_ciphertext"])
    nonce = base64.b64decode(encrypted_data["e2e_nonce"])
    
    # Split ciphertext and tag (last 16 bytes)
    tag = ciphertext[-16:]
    actual_ciphertext = ciphertext[:-16]
    
    # Decrypt
    cipher = ChaCha20_Poly1305.new(key=session_key, nonce=nonce)
    cipher.update(b"")
    plaintext = cipher.decrypt_and_verify(actual_ciphertext, tag)
    
    return plaintext
```

### 8.2 Forward Secrecy

**实现方式**：每次会话使用新的临时密钥对（Ephemeral Key Pair）。

- 会话结束后，临时私钥**立即销毁**
- 即使长期私钥泄露，历史消息也无法解密

### 8.3 密钥托管（可选）

人类用户可以选择将 E2E 密钥托管给可信第三方（如 Maian OS）：

```json
{
  "type": "e2e.key_escrow",
  "escrow_provider": "did:anp:agent:maian-escrow-001",
  "encrypted_key_material": "base64-encoded-encrypted-key..."
}
```

**注意**：密钥托管是**可选功能**，默认不启用。

---

## 9. 信誉与评级

### 9.1 人类信誉模型

复用 Phase 3.6 的互评协议，增加人类专属维度。

#### 9.1.1 评级维度

| 维度 | 说明 | 权重 |
|------|------|------|
| **响应速度** | 平均回复时间 | 20% |
| **消息质量** | 是否有意义（防 spam） | 30% |
| **社区贡献** | 是否帮助其他用户 | 25% |
| **信誉历史** | 长期行为记录 | 25% |

#### 9.1.2 计算公式

```python
def calculate_human_reputation(ratings: list[Rating]) -> float:
    """
    Calculate human reputation score (0-100).
    """
    if len(ratings) == 0:
        return 50.0  # Neutral score for new users
    
    # Weighted average of ratings
    total_weight = 0
    weighted_sum = 0
    
    for r in ratings:
        weight = get_rating_weight(r.dimension)
        weighted_sum += r.score * weight
        total_weight += weight
    
    avg_score = weighted_sum / total_weight
    
    # Apply time decay (older ratings matter less)
    decayed_score = apply_time_decay(avg_score, ratings)
    
    return max(0.0, min(100.0, decayed_score))
```

### 9.2 评级端点

**POST /api/v1/rating**（复用 Phase 3.6）

**请求体**（人类评级示例）：

```json
{
  "task_id": "msg-20260801-001",
  "caller_did": "did:anp:human:alice...",
  "target_did": "did:anp:agent:bob...",
  "rating": 5,
  "rating_note": "回答很专业，速度快",
  "dimensions": {
    "response_speed": 5,
    "message_quality": 5,
    "community_contribution": 4
  }
}
```

---

## 10. API 端点规范

### 10.1 人类专属端点

#### 10.1.1 `POST /api/v1/human/register`

注册人类身份，生成 HumanCard。

**请求体**：

```json
{
  "name": "Alice Chen",
  "display_name": "Alice",
  "bio": "AI product designer",
  "languages": ["zh-CN", "en-US"],
  "timezone": "Asia/Shanghai",
  "avatar_url": "https://..."
}
```

**响应**：

```json
{
  "success": true,
  "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
  "human_card": { ... },
  "private_key": "base64-encoded-private-key (ONLY returned once!)"
}
```

**⚠️ 重要**：`private_key` 只返回一次，客户端必须安全存储！

#### 10.1.2 `POST /api/v1/human/message`

发送消息。

**请求体**：

```json
{
  "recipient_did": "did:anp:agent:xxx",
  "text": "帮我分析 BTC 走势",
  "attachments": [],
  "reply_to": null,
  "enable_e2e": true
}
```

**响应**：

```json
{
  "success": true,
  "message_id": "msg-20260801-001-abc123",
  "timestamp": "2026-08-01T12:00:00.000Z",
  "status": "sent"
}
```

#### 10.1.3 `GET /api/v1/human/messages`

获取历史消息（分页）。

**查询参数**：

| 参数 | 类型 | 说明 |
|------|------|------|
| `conversation_id` | string | 会话 ID（可选，不传则返回所有消息） |
| `before` | string | 返回此消息 ID 之前的消息 |
| `after` | string | 返回此消息 ID 之后的消息 |
| `limit` | int | 每页数量（默认 50，最大 200） |

**响应**：

```json
{
  "messages": [ ... ],
  "has_more": true,
  "next_cursor": "msg-20260801-00042-abc123"
}
```

#### 10.1.4 `POST /api/v1/human/presence`

更新 Presence 状态（见 Section 6.2.1）。

#### 10.1.5 `GET /api/v1/human/profile`

获取人类资料。

**响应**：

```json
{
  "did": "did:anp:human:xxx",
  "name": "Alice Chen",
  "display_name": "Alice",
  "avatar_url": "https://...",
  "bio": "...",
  "reputation": 85.5,
  "total_ratings": 42,
  "presence": { ... }
}
```

#### 10.1.6 `PUT /api/v1/human/profile`

更新人类资料。

### 10.2 扩展现有端点

#### 10.2.1 `GET /api/v1/agentcard/discover`

增加 `type` 参数（见 Section 4.3.2）。

#### 10.2.2 `POST /api/v1/agentcard/import`

支持导入 HumanCard（YAML/JSON 格式相同，自动识别 `type: "human"`）。

---

## 11. 错误码规范

### 11.1 错误码列表

| 错误码 | 名称 | HTTP 状态码 | 说明 |
|--------|------|-------------|------|
| `HUMAN_ERR_INVALID_DID` | 无效 Human DID | 400 | DID 格式错误 |
| `HUMAN_ERR_DID_EXISTS` | DID 已存在 | 409 | 注册时 DID 冲突 |
| `HUMAN_ERR_NOT_FOUND` | 人类未找到 | 404 | DID 不存在 |
| `HUMAN_ERR_NO_ROUTE` | 无法路由 | 503 | 所有连接尝试失败 |
| `HUMAN_ERR_E2E_FAILED` | E2E 加密失败 | 500 | 密钥交换或解密失败 |
| `HUMAN_ERR_MESSAGE_TOO_LONG` | 消息过长 | 413 | 超过 65536 字节 |
| `HUMAN_ERR_RATE_LIMIT` | 频率限制 | 429 | 超过每分钟 60 条 |
| `HUMAN_ERR_SPAM_DETECTED` | 检测到 Spam | 403 | 信誉过低或内容违规 |
| `HUMAN_ERR_INVALID_RECIPIENT` | 无效接收者 | 400 | recipient_did 不存在或离线 |

### 11.2 错误响应格式

```json
{
  "error": {
    "code": "HUMAN_ERR_NO_ROUTE",
    "message": "Cannot route to recipient: all connection attempts failed",
    "details": {
      "recipient_did": "did:anp:human:xxx",
      "attempts": [
        { "method": "direct", "error": "connection refused" },
        { "method": "relay", "error": "no available relay" }
      ]
    }
  }
}
```

---

## 12. 安全模型

### 12.1 威胁模型

| 威胁 | 描述 | 缓解措施 |
|------|------|----------|
| **身份冒充** | 攻击者冒充人类 | DID + Ed25519 签名 |
| **消息窃听** | 中间人攻击 | E2E 加密（ChaCha20-Poly1305） |
| **Spam** | 大量垃圾消息 | 频率限制 + 信誉系统 |
| **Sybil 攻击** | 大量虚假身份 | 需要 KOAN Chain 抵押（未来） |
| **DID 劫持** | 私钥泄露 | 密钥托管 + 社交恢复（未来） |

### 12.2 隐私保护

#### 12.2.1 元数据最小化

- Presence 状态不包含精确位置
- 消息不携带 IP 地址
- 用户可选择隐藏 `last_active` 时间

#### 12.2.2 消息过期

可选功能（v1.1）：消息在指定时间后自动删除。

```json
{
  "ttl": 3600  // 1 小时后过期
}
```

#### 12.2.3 匿名模式（未来 v2.0）

人类可以选择匿名发送消息（不暴露 DID）。

---

## 13. 实现清单

### 13.1 Soraecho 侧（协议层）

#### 必须实现（P0）

- [ ] AgentCard 增加 `type` 字段（`"human"` / `"agent"`）
- [ ] 新增 `chat.message` A2A 消息类型处理
- [ ] 新增 `POST /api/v1/human/register` 端点
- [ ] 新增 `POST /api/v1/human/message` 端点
- [ ] 新增 `GET /api/v1/human/messages` 端点
- [ ] 新增 `POST /api/v1/human/presence` 端点
- [ ] `discover` 端点支持 `type` 过滤
- [ ] Presence 状态管理（内存 + 持久化）

#### 推荐实现（P1）

- [ ] E2E 加密扩展（Forward Secrecy）
- [ ] `chat.receipt` 消息类型（已送达/已读）
- [ ] `chat.typing` 消息类型（正在输入）
- [ ] 消息序列号和去重
- [ ] 频率限制（每分钟 60 条）

#### 可选实现（P2）

- [ ] 消息附件支持（IPFS 上传）
- [ ] `chat.reaction` 消息类型
- [ ] 会话管理（Conversation）
- [ ] 人类 DID 生成 CLI 工具（`anp human register`）

### 13.2 Maian OS 侧（运行时）

#### 必须实现（P0）

- [ ] HumanAgent 代理节点实现
- [ ] 人类客户端（App/Web）
- [ ] 私钥安全存储（Keychain/Keystore）
- [ ] Presence 自动更新
- [ ] 消息发送/接收 UI

#### 推荐实现（P1）

- [ ] 消息通知系统（Push Notification）
- [ ] E2E 加密集成
- [ ] 媒体处理（图片预览/语音播放）
- [ ] Markdown 渲染

#### 可选实现（P2）

- [ ] 多端同步（手机/桌面/Web）
- [ ] 消息搜索
- [ ] 语音输入
- [ ] 主题/个性化

---

## 14. 测试用例

### 14.1 互操作性测试

#### 测试 1：基本消息发送

```
GIVEN: Human Node A 和 Agent Node B 在线
WHEN:  A 发送 chat.message 给 B
THEN:  B 收到消息，返回 chat.receipt
AND:   A 收到 receipt，状态变为 DELIVERED
```

#### 测试 2：E2E 加密

```
GIVEN: Human A 和 Human B 支持 E2E
WHEN:  A 发送 enable_e2e=true 的消息
THEN:  消息在网络上加密传输
AND:   B 能成功解密
AND:   中间节点无法解密
```

#### 测试 3：Presence 更新

```
GIVEN: Human A 在线（available）
WHEN:  A 调用 POST /api/v1/human/presence {status: "away"}
THEN:  A 的 presence.status 变为 "away"
AND:   订阅 A 的节点收到 presence.update 推送
```

#### 测试 4：发现人类用户

```
GIVEN: 网络上有 3 个人类用户和 10 个 Agent
WHEN:  调用 GET /api/v1/agentcard/discover?type=human
THEN:  只返回 3 个人类用户
```

#### 测试 5：频率限制

```
GIVEN: Human A 信誉正常
WHEN:  A 在 1 分钟内发送 61 条消息
THEN:  第 61 条消息返回 HUMAN_ERR_RATE_LIMIT
```

---

## 15. 附录

### 15.1 完整 HumanCard 示例

```json
{
  "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
  "type": "human",
  "name": "Alice Chen",
  "version": "1.0.0",
  "description": "AI product designer and Soraecho user",
  "capabilities": [
    "chat",
    "human.presence",
    "human.rating",
    "e2e.encryption"
  ],
  "endpoints": [
    "/api/v1/human/message"
  ],
  "public_key_ed25519": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
  "lifecycle_state": "active",
  "last_heartbeat": "2026-08-01T12:00:00.000Z",
  "metadata": {
    "human_languages": ["zh-CN", "en-US"],
    "timezone": "Asia/Shanghai",
    "avatar_url": "https://soraecho.com/avatars/alice.jpg",
    "display_name": "Alice",
    "bio": "Designing the future of AI-human interaction",
    "social_links": {
      "github": "https://github.com/alicechen",
      "twitter": "https://twitter.com/alicechen_ai"
    }
  },
  "os_did": "did:maian:user:alice-001",
  "chain_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD38",
  "supports_settlement": true,
  "created_at": "2026-08-01T10:00:00.000Z",
  "updated_at": "2026-08-01T12:00:00.000Z"
}
```

### 15.2 完整 chat.message 示例

```json
{
  "message_id": "msg-20260801-00042-a1b2c3d4",
  "type": "chat.message",
  "version": "1.0.0",
  "sender": {
    "did": "did:anp:human:1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R9S0T",
    "type": "human",
    "display_name": "Alice Chen",
    "avatar_url": "https://soraecho.com/avatars/alice.jpg"
  },
  "recipient": {
    "did": "did:anp:1AiNWNKy2zWZacZBTMFkNkKHiWZvhHsvdKr1Es7c8PzXz7",
    "type": "agent"
  },
  "payload": {
    "text": "帮我分析一下 **BTC 的走势** @PriceOracle，谢谢！",
    "text_format": "markdown",
    "attachments": [
      {
        "type": "image",
        "url": "ipfs://QmYwAPJzv5CZsnA7RdHjXdzjMxGqDqK5qJb2q7dZ5ZJvKJ",
        "thumbnail_url": "ipfs://Qm...",
        "filename": "btc-chart.png",
        "size_bytes": 102400,
        "mime_type": "image/png",
        "width": 1200,
        "height": 800,
        "caption": "BTC 价格图（2026-07）"
      }
    ],
    "mentions": [
      {
        "did": "did:anp:agent:price-oracle-001",
        "display_name": "PriceOracle",
        "index": 15,
        "length": 12
      }
    ],
    "reply_to": {
      "message_id": "msg-20260731-042-xyz789",
      "sender_did": "did:anp:1AiNWNKy2zWZacZBTMFkNkKHiWZvhHsvdKr1Es7c8PzXz7",
      "preview_text": "好的，我来帮你分析最近的趋势..."
    }
  },
  "timestamp": "2026-08-01T12:00:00.000Z",
  "message_sequence": 42,
  "enable_e2e": true,
  "e2e_ciphertext": "YmFzZTY0LWVuY29kZWQtY2lwaGVydGV4dA==",
  "e2e_nonce": "YmFzZTY0LWVuY29kZWQtbm9uY2U=",
  "e2e_sender_ephemeral_pub": "YmFzZTY0LWVuY29kZWQtcHVia2V5",
  "ttl": 86400
}
```

### 15.3 参考实现（伪代码）

#### 发送消息（Sender Side）

```python
def send_message(sender_human: HumanCard, recipient_did: str, text: str, enable_e2e: bool) -> str:
    """
    Send a chat.message from sender to recipient.
    """
    # Step 1: Lookup recipient
    recipient_card = discover_peer(recipient_did)
    if recipient_card is None:
        raise Exception("HUMAN_ERR_NOT_FOUND")
    
    # Step 2: Create message
    message = {
        "message_id": generate_message_id(sender_human.did),
        "type": "chat.message",
        "version": "1.0.0",
        "sender": {
            "did": sender_human.did,
            "type": "human",
            "display_name": sender_human.metadata.get("display_name")
        },
        "recipient": {
            "did": recipient_did,
            "type": recipient_card.type
        },
        "payload": {
            "text": text,
            "text_format": "markdown"
        },
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "enable_e2e": enable_e2e
    }
    
    # Step 3: E2E encryption (if enabled)
    if enable_e2e:
        session_key = establish_e2e_session(sender_human, recipient_card)
        plaintext = json.dumps(message["payload"]).encode('utf-8')
        encrypted = encrypt_message(plaintext, session_key)
        message.update(encrypted)
        del message["payload"]  # Remove plaintext payload
    
    # Step 4: Send via A2A protocol
    response = a2a_send_message(recipient_did, message)
    
    # Step 5: Return message ID
    return message["message_id"]
```

#### 接收消息（Receiver Side）

```python
def on_message_received(message: dict):
    """
    Handle incoming chat.message.
    """
    # Step 1: Validate message structure
    if message["type"] != "chat.message":
        return
    
    # Step 2: Decrypt if E2E enabled
    if message.get("enable_e2e"):
        session_key = get_e2e_session(message["sender"]["did"])
        decrypted_payload = decrypt_message(message, session_key)
        message["payload"] = json.loads(decrypted_payload)
    
    # Step 3: Store message locally
    conversation_id = get_or_create_conversation(
        participant_dids=[message["sender"]["did"], message["recipient"]["did"]]
    )
    store_message(conversation_id, message)
    
    # Step 4: Send delivery receipt
    send_receipt(message["message_id"], status="delivered")
    
    # Step 5: Notify UI
    notify_user(message)
```

### 15.4 版本历史

| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0.0-draft | 2026-08-01 | 初稿 |
| 1.0.0-draft.2 | 2026-08-01 | 详细化：JSON Schema、伪代码、测试用例、安全模型 |

---

**End of Specification**

**Contact**: soraecho Core Team <sora@soraecho.com>  
**Discussion**: https://soraecho.com/community
