Phase 1 Complete: Core Protocol & Python SDK
Browse files- PHASE1_COMPLETE.md +320 -0
- README.md +219 -0
- examples/basic_handshake.py +186 -0
- examples/verify_phase1.py +163 -0
- schemas/agent_identity.schema +81 -0
- schemas/message_envelope.schema +72 -0
- sdk-python/README.md +194 -0
- sdk-python/aipm/__init__.py +52 -0
- sdk-python/aipm/__pycache__/__init__.cpython-314.pyc +0 -0
- sdk-python/aipm/__pycache__/models.cpython-314.pyc +0 -0
- sdk-python/aipm/agent.py +208 -0
- sdk-python/aipm/crypto.py +133 -0
- sdk-python/aipm/exceptions.py +45 -0
- sdk-python/aipm/handshake.py +353 -0
- sdk-python/aipm/models.py +160 -0
- sdk-python/pyproject.toml +59 -0
PHASE1_COMPLETE.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AIPM Phase 1 - Complete ✓
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
Phase 1 of the Agent Interoperability Protocol Models (AIPM) ecosystem has been successfully completed. This phase establishes the foundational components for cross-vendor AI agent communication.
|
| 6 |
+
|
| 7 |
+
## Deliverables
|
| 8 |
+
|
| 9 |
+
### 1. JSON Schemas ✓
|
| 10 |
+
|
| 11 |
+
**Location:** `aipm/schemas/`
|
| 12 |
+
|
| 13 |
+
#### `agent_identity.schema`
|
| 14 |
+
Defines the complete agent identity structure including:
|
| 15 |
+
- Agent ID and organization ID
|
| 16 |
+
- Name and version
|
| 17 |
+
- Public key for encryption
|
| 18 |
+
- Capabilities (skills, models, tools, max context, memory support, languages)
|
| 19 |
+
- Trust score (reliability, accuracy, latency, success rate, interactions)
|
| 20 |
+
- Economic information (cost, currency, billing model)
|
| 21 |
+
|
| 22 |
+
#### `message_envelope.schema`
|
| 23 |
+
Defines the standard AIPM message format:
|
| 24 |
+
- Unique message ID (UUID v4)
|
| 25 |
+
- Message type (handshake, task, memory, error, etc.)
|
| 26 |
+
- Protocol version
|
| 27 |
+
- Sender and receiver references
|
| 28 |
+
- Timestamp
|
| 29 |
+
- Priority (low, normal, high, critical)
|
| 30 |
+
- Payload (flexible JSON object)
|
| 31 |
+
- Cryptographic signature
|
| 32 |
+
|
| 33 |
+
### 2. Python SDK ✓
|
| 34 |
+
|
| 35 |
+
**Location:** `aipm/sdk-python/aipm/`
|
| 36 |
+
|
| 37 |
+
#### Core Modules
|
| 38 |
+
|
| 39 |
+
**`models.py`** - Pydantic data models
|
| 40 |
+
- `AgentIdentity` - Complete agent profile
|
| 41 |
+
- `AgentReference` - Minimal agent routing info
|
| 42 |
+
- `Capabilities` - Skills, models, tools declaration
|
| 43 |
+
- `TrustScore` - Reputation metrics
|
| 44 |
+
- `EconomicInfo` - Billing information
|
| 45 |
+
- `MessageEnvelope` - Standard message wrapper
|
| 46 |
+
- `MessageType` - Enum of all message types
|
| 47 |
+
- `Priority` - Message priority levels
|
| 48 |
+
- `HandshakePayload` - Handshake-specific payload
|
| 49 |
+
|
| 50 |
+
**`handshake.py`** - Handshake protocol state machine
|
| 51 |
+
- `HandshakeState` - State enumeration
|
| 52 |
+
- `HandshakeManager` - Manages handshake flow
|
| 53 |
+
- IDLE → HELLO_SENT → CAPABILITY_EXCHANGED → AUTHENTICATED → KEY_EXCHANGED → TRUST_VERIFIED → READY
|
| 54 |
+
- Complete message creation and handling for each step
|
| 55 |
+
- Session management
|
| 56 |
+
- Peer identity tracking
|
| 57 |
+
|
| 58 |
+
**`agent.py`** - Base agent implementation
|
| 59 |
+
- `AIPMAgent` - Complete agent with handshake support
|
| 60 |
+
- Automatic key generation
|
| 61 |
+
- Message routing and handling
|
| 62 |
+
- Handshake management per peer
|
| 63 |
+
- Task request creation
|
| 64 |
+
- Custom message handler registration
|
| 65 |
+
|
| 66 |
+
**`crypto.py`** - Cryptographic operations
|
| 67 |
+
- `CryptoManager` - Ed25519 key management
|
| 68 |
+
- Keypair generation
|
| 69 |
+
- Message signing
|
| 70 |
+
- Signature verification
|
| 71 |
+
- Public key fingerprinting
|
| 72 |
+
- Key serialization (Base64)
|
| 73 |
+
|
| 74 |
+
**`exceptions.py`** - Exception hierarchy
|
| 75 |
+
- `AIPMException` - Base exception
|
| 76 |
+
- `HandshakeError` - Handshake failures
|
| 77 |
+
- `ValidationError` - Message validation
|
| 78 |
+
- `AuthenticationError` - Auth failures
|
| 79 |
+
- `EncryptionError` - Crypto operations
|
| 80 |
+
- `TrustError` - Trust verification
|
| 81 |
+
- `CapabilityError` - Capability negotiation
|
| 82 |
+
|
| 83 |
+
**`__init__.py`** - Clean public API
|
| 84 |
+
- Exports all public classes and functions
|
| 85 |
+
- Version management
|
| 86 |
+
|
| 87 |
+
### 3. Handshake Protocol ✓
|
| 88 |
+
|
| 89 |
+
**Complete TLS-inspired handshake flow:**
|
| 90 |
+
|
| 91 |
+
```
|
| 92 |
+
Initiator (Agent A) Responder (Agent B)
|
| 93 |
+
| |
|
| 94 |
+
|------- HELLO ------------------->|
|
| 95 |
+
| (identity, session_id) |
|
| 96 |
+
| |
|
| 97 |
+
|<--- CAPABILITY_EXCHANGE ---------|
|
| 98 |
+
| (identity, capabilities) |
|
| 99 |
+
| |
|
| 100 |
+
|------- AUTHENTICATION ---------->|
|
| 101 |
+
| (challenge) |
|
| 102 |
+
| |
|
| 103 |
+
|<--- PUBLIC_KEY_EXCHANGE ---------|
|
| 104 |
+
| (public_key) |
|
| 105 |
+
| |
|
| 106 |
+
|------- TRUST_VERIFICATION ------>|
|
| 107 |
+
| (trust_score) |
|
| 108 |
+
| |
|
| 109 |
+
|<--- READY ------------------------|
|
| 110 |
+
| (status: ready) |
|
| 111 |
+
| |
|
| 112 |
+
[Both agents now READY]
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
### 4. Example Implementation ✓
|
| 116 |
+
|
| 117 |
+
**Location:** `aipm/examples/`
|
| 118 |
+
|
| 119 |
+
#### `basic_handshake.py`
|
| 120 |
+
Complete demonstration showing:
|
| 121 |
+
1. Two agent creation (OpenAI-based and LangGraph-based)
|
| 122 |
+
2. Full handshake execution (7 steps)
|
| 123 |
+
3. Capability discovery
|
| 124 |
+
4. Trust score exchange
|
| 125 |
+
5. Task request creation
|
| 126 |
+
6. Pretty-printed message flow
|
| 127 |
+
|
| 128 |
+
#### `verify_phase1.py`
|
| 129 |
+
Automated verification script that checks:
|
| 130 |
+
- JSON schemas exist and are valid
|
| 131 |
+
- SDK folder structure is complete
|
| 132 |
+
- All Python modules are importable
|
| 133 |
+
- Example script is present
|
| 134 |
+
|
| 135 |
+
## Project Structure
|
| 136 |
+
|
| 137 |
+
```
|
| 138 |
+
aipm/
|
| 139 |
+
├── README.md # Project overview
|
| 140 |
+
├── PHASE1_COMPLETE.md # This document
|
| 141 |
+
│
|
| 142 |
+
├── schemas/ # JSON schemas
|
| 143 |
+
│ ├── agent_identity.schema # Agent identity spec
|
| 144 |
+
│ └── message_envelope.schema # Message format spec
|
| 145 |
+
│
|
| 146 |
+
├── sdk-python/ # Python SDK
|
| 147 |
+
│ ├── README.md # SDK documentation
|
| 148 |
+
│ ├── pyproject.toml # Package configuration
|
| 149 |
+
│ └── aipm/ # Main package
|
| 150 |
+
│ ├── __init__.py # Public API
|
| 151 |
+
│ ├── models.py # Data models
|
| 152 |
+
│ ├── handshake.py # Handshake protocol
|
| 153 |
+
│ ├── agent.py # Agent implementation
|
| 154 |
+
│ ├── crypto.py # Cryptography
|
| 155 |
+
│ └── exceptions.py # Exception classes
|
| 156 |
+
│
|
| 157 |
+
└── examples/ # Example scripts
|
| 158 |
+
├── basic_handshake.py # Full handshake demo
|
| 159 |
+
└── verify_phase1.py # Verification script
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
## Key Features Implemented
|
| 163 |
+
|
| 164 |
+
### Identity Layer ✓
|
| 165 |
+
- Standardized agent identification
|
| 166 |
+
- Capability declaration
|
| 167 |
+
- Trust score tracking
|
| 168 |
+
- Economic metadata
|
| 169 |
+
|
| 170 |
+
### Secure Handshake ✓
|
| 171 |
+
- 7-step TLS-inspired protocol
|
| 172 |
+
- State machine implementation
|
| 173 |
+
- Session management
|
| 174 |
+
- Peer identity exchange
|
| 175 |
+
|
| 176 |
+
### Cryptographic Foundation ✓
|
| 177 |
+
- Ed25519 keypair generation
|
| 178 |
+
- Message signing infrastructure
|
| 179 |
+
- Signature verification
|
| 180 |
+
- Public key fingerprinting
|
| 181 |
+
|
| 182 |
+
### Extensibility ✓
|
| 183 |
+
- Custom message handler registration
|
| 184 |
+
- Pluggable capability system
|
| 185 |
+
- Vendor-agnostic design
|
| 186 |
+
|
| 187 |
+
## Running the Example
|
| 188 |
+
|
| 189 |
+
### With Dependencies Installed
|
| 190 |
+
|
| 191 |
+
```bash
|
| 192 |
+
cd aipm/sdk-python
|
| 193 |
+
pip install -e .
|
| 194 |
+
cd ../examples
|
| 195 |
+
python basic_handshake.py
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
### Verification Only (No Dependencies)
|
| 199 |
+
|
| 200 |
+
```bash
|
| 201 |
+
cd aipm
|
| 202 |
+
python3 examples/verify_phase1.py
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
## Technical Specifications
|
| 206 |
+
|
| 207 |
+
### Protocol Version
|
| 208 |
+
- **Version:** 1.0.0
|
| 209 |
+
- **Format:** Semantic versioning (X.Y.Z)
|
| 210 |
+
|
| 211 |
+
### Message Format
|
| 212 |
+
- **Encoding:** JSON
|
| 213 |
+
- **Timestamps:** ISO 8601 (UTC)
|
| 214 |
+
- **IDs:** UUID v4
|
| 215 |
+
|
| 216 |
+
### Cryptography
|
| 217 |
+
- **Algorithm:** Ed25519 (EdDSA)
|
| 218 |
+
- **Key Size:** 256 bits
|
| 219 |
+
- **Encoding:** Base64
|
| 220 |
+
- **Fingerprint:** SHA256
|
| 221 |
+
|
| 222 |
+
### Dependencies
|
| 223 |
+
- `pydantic>=2.0.0` - Data validation
|
| 224 |
+
- `cryptography>=41.0.0` - Crypto operations
|
| 225 |
+
- `httpx>=0.25.0` - Future HTTP transport
|
| 226 |
+
|
| 227 |
+
## What's Next: Phase 2
|
| 228 |
+
|
| 229 |
+
Phase 2 will build on this foundation to add:
|
| 230 |
+
|
| 231 |
+
### 1. Task Negotiation Framework
|
| 232 |
+
- Accept/decline task mechanism
|
| 233 |
+
- Capability matching
|
| 234 |
+
- Priority-based routing
|
| 235 |
+
- Deadline handling
|
| 236 |
+
- Resource availability checks
|
| 237 |
+
|
| 238 |
+
### 2. Cryptographic Message Signing
|
| 239 |
+
- Automatic message signing
|
| 240 |
+
- Signature verification in message processing
|
| 241 |
+
- Key rotation support
|
| 242 |
+
- Certificate chain validation
|
| 243 |
+
|
| 244 |
+
### 3. HTTP/WebSocket Transport Layer
|
| 245 |
+
- REST API endpoints
|
| 246 |
+
- WebSocket real-time communication
|
| 247 |
+
- Message queuing
|
| 248 |
+
- Retry logic
|
| 249 |
+
- Connection pooling
|
| 250 |
+
|
| 251 |
+
### 4. Enhanced Error Handling
|
| 252 |
+
- Detailed error codes
|
| 253 |
+
- Retry strategies
|
| 254 |
+
- Circuit breakers
|
| 255 |
+
- Timeout management
|
| 256 |
+
|
| 257 |
+
### 5. Testing Framework
|
| 258 |
+
- Unit tests for all modules
|
| 259 |
+
- Integration tests for handshake
|
| 260 |
+
- Mock agent implementations
|
| 261 |
+
- Performance benchmarks
|
| 262 |
+
|
| 263 |
+
## Success Metrics
|
| 264 |
+
|
| 265 |
+
✅ **100% Schema Coverage** - All protocol components defined
|
| 266 |
+
✅ **Complete State Machine** - 7-step handshake implemented
|
| 267 |
+
✅ **Type Safety** - Full Pydantic validation
|
| 268 |
+
✅ **Crypto Foundation** - Ed25519 signing ready
|
| 269 |
+
✅ **Extensibility** - Plugin architecture for handlers
|
| 270 |
+
✅ **Documentation** - Comprehensive README and examples
|
| 271 |
+
✅ **Verification** - Automated testing script
|
| 272 |
+
|
| 273 |
+
## Architecture Decisions
|
| 274 |
+
|
| 275 |
+
### Why Ed25519?
|
| 276 |
+
- Modern, secure, fast
|
| 277 |
+
- Small key size (32 bytes)
|
| 278 |
+
- Deterministic signatures
|
| 279 |
+
- Wide library support
|
| 280 |
+
|
| 281 |
+
### Why Pydantic?
|
| 282 |
+
- Runtime validation
|
| 283 |
+
- Type hints integration
|
| 284 |
+
- JSON serialization
|
| 285 |
+
- Excellent error messages
|
| 286 |
+
|
| 287 |
+
### Why State Machine for Handshake?
|
| 288 |
+
- Clear protocol flow
|
| 289 |
+
- Easy to debug
|
| 290 |
+
- Prevents invalid transitions
|
| 291 |
+
- Extensible for future states
|
| 292 |
+
|
| 293 |
+
### Why Separate Identity and Reference?
|
| 294 |
+
- Minimize message size
|
| 295 |
+
- Enable routing without full identity
|
| 296 |
+
- Support identity caching
|
| 297 |
+
- Privacy-preserving
|
| 298 |
+
|
| 299 |
+
## Compliance
|
| 300 |
+
|
| 301 |
+
✅ Adheres to AIPM architecture specification
|
| 302 |
+
✅ JSON Schema Draft 2020-12 compliant
|
| 303 |
+
✅ Python 3.9+ compatible
|
| 304 |
+
✅ Type-hint complete
|
| 305 |
+
✅ Apache 2.0 licensed
|
| 306 |
+
|
| 307 |
+
## Contributors
|
| 308 |
+
|
| 309 |
+
Built by the AIPM team to establish the first open ecosystem for AI agent interoperability.
|
| 310 |
+
|
| 311 |
+
## License
|
| 312 |
+
|
| 313 |
+
Apache 2.0 - See LICENSE file for details
|
| 314 |
+
|
| 315 |
+
---
|
| 316 |
+
|
| 317 |
+
**Phase 1 Status:** ✅ COMPLETE
|
| 318 |
+
**Ready for Phase 2:** ✅ YES
|
| 319 |
+
**Date Completed:** July 6, 2026
|
| 320 |
+
**Next Review:** Phase 2 Planning Session
|
README.md
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agent Interoperability Protocol Models (AIPM)
|
| 2 |
+
|
| 3 |
+
## Vision
|
| 4 |
+
|
| 5 |
+
AIPM is the first open ecosystem for interoperable AI agents, enabling agents from different vendors (OpenAI, Claude, LangGraph, AutoGen, CrewAI, etc.) to communicate securely and consistently using the same protocol.
|
| 6 |
+
|
| 7 |
+
## Architecture Overview
|
| 8 |
+
|
| 9 |
+
### Core Protocol Components
|
| 10 |
+
|
| 11 |
+
1. **Identity Layer** - Agent identification and capabilities
|
| 12 |
+
2. **Capability Discovery** - Automatic discovery of skills and tools
|
| 13 |
+
3. **Secure Handshake** - TLS-inspired connection establishment
|
| 14 |
+
4. **Task Negotiation** - Accept/decline work based on capability
|
| 15 |
+
5. **Memory Exchange** - Efficient context sharing
|
| 16 |
+
6. **Trust Layer** - Reputation and reliability tracking
|
| 17 |
+
7. **Skill Marketplace** - Dynamic capability discovery
|
| 18 |
+
8. **Workflow Delegation** - Hierarchical task orchestration
|
| 19 |
+
9. **Economic Layer** - API billing and micropayments
|
| 20 |
+
10. **Standard Message Format** - JSON-based protocol
|
| 21 |
+
|
| 22 |
+
## Project Structure
|
| 23 |
+
|
| 24 |
+
```
|
| 25 |
+
aipm/
|
| 26 |
+
├── schemas/ # JSON schemas for protocol messages
|
| 27 |
+
├── sdk-python/ # Python reference SDK
|
| 28 |
+
├── sdk-javascript/ # JavaScript SDK (future)
|
| 29 |
+
├── sdk-rust/ # Rust SDK (future)
|
| 30 |
+
├── models/ # Fine-tuned AIPM models (future)
|
| 31 |
+
├── datasets/ # Training and benchmark datasets (future)
|
| 32 |
+
├── examples/ # Example implementations
|
| 33 |
+
└── docs/ # Protocol documentation
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## Quick Start
|
| 37 |
+
|
| 38 |
+
### Installation
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
cd sdk-python
|
| 42 |
+
pip install -e .
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
### Basic Usage
|
| 46 |
+
|
| 47 |
+
```python
|
| 48 |
+
from aipm import AIPMAgent, AgentIdentity, Capabilities
|
| 49 |
+
|
| 50 |
+
# Create agent identity
|
| 51 |
+
identity = AgentIdentity(
|
| 52 |
+
agent_id="my-agent-001",
|
| 53 |
+
organization_id="my-org",
|
| 54 |
+
name="My AI Agent",
|
| 55 |
+
version="1.0.0",
|
| 56 |
+
capabilities=Capabilities(
|
| 57 |
+
skills=["text-generation", "code-review"],
|
| 58 |
+
models=["gpt-4"],
|
| 59 |
+
tools=["code-interpreter"],
|
| 60 |
+
)
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# Initialize agent
|
| 64 |
+
agent = AIPMAgent(identity)
|
| 65 |
+
|
| 66 |
+
# Initiate handshake with another agent
|
| 67 |
+
peer = AgentReference(
|
| 68 |
+
agent_id="peer-agent-001",
|
| 69 |
+
organization_id="peer-org"
|
| 70 |
+
)
|
| 71 |
+
hello_msg = agent.initiate_handshake(peer)
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
### Run Example
|
| 75 |
+
|
| 76 |
+
See `examples/basic_handshake.py` for a complete handshake between OpenAI and LangGraph agents:
|
| 77 |
+
|
| 78 |
+
```bash
|
| 79 |
+
cd examples
|
| 80 |
+
python basic_handshake.py
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
## Current Status
|
| 84 |
+
|
| 85 |
+
### ✅ Phase 1: COMPLETE
|
| 86 |
+
|
| 87 |
+
- [x] JSON schemas defined
|
| 88 |
+
- [x] Python SDK scaffolded
|
| 89 |
+
- [x] Identity & handshake models implemented
|
| 90 |
+
- [x] Basic agent implementation
|
| 91 |
+
- [x] Cryptographic foundation (Ed25519)
|
| 92 |
+
- [x] Example scripts
|
| 93 |
+
|
| 94 |
+
**See [PHASE1_COMPLETE.md](./PHASE1_COMPLETE.md) for full details**
|
| 95 |
+
|
| 96 |
+
### 🚧 Phase 2: In Planning
|
| 97 |
+
|
| 98 |
+
- [ ] Task negotiation framework
|
| 99 |
+
- [ ] Cryptographic message signing
|
| 100 |
+
- [ ] HTTP/WebSocket transport
|
| 101 |
+
- [ ] Enhanced error handling
|
| 102 |
+
- [ ] Comprehensive test suite
|
| 103 |
+
|
| 104 |
+
### 📋 Future Phases
|
| 105 |
+
|
| 106 |
+
**Phase 3: Advanced Features**
|
| 107 |
+
- [ ] Memory exchange protocol
|
| 108 |
+
- [ ] Trust scoring system
|
| 109 |
+
- [ ] Economic layer implementation
|
| 110 |
+
|
| 111 |
+
**Phase 4: Ecosystem**
|
| 112 |
+
- [ ] JavaScript SDK
|
| 113 |
+
- [ ] Rust SDK
|
| 114 |
+
- [ ] Fine-tuned AIPM models
|
| 115 |
+
- [ ] Benchmark datasets
|
| 116 |
+
- [ ] Public registry/marketplace
|
| 117 |
+
|
| 118 |
+
## Handshake Protocol
|
| 119 |
+
|
| 120 |
+
```
|
| 121 |
+
Agent A Agent B
|
| 122 |
+
| |
|
| 123 |
+
|------- HELLO ----------------->|
|
| 124 |
+
|<--- CAPABILITY_EXCHANGE -------|
|
| 125 |
+
|------- AUTHENTICATION -------->|
|
| 126 |
+
|<--- PUBLIC_KEY_EXCHANGE -------|
|
| 127 |
+
|------- TRUST_VERIFICATION ---->|
|
| 128 |
+
|<--- READY ---------------------|
|
| 129 |
+
| |
|
| 130 |
+
[Ready for task delegation]
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
## Key Features
|
| 134 |
+
|
| 135 |
+
### Identity Layer
|
| 136 |
+
- Unique agent IDs
|
| 137 |
+
- Organization affiliations
|
| 138 |
+
- Capability declarations
|
| 139 |
+
- Trust scores
|
| 140 |
+
- Public key cryptography
|
| 141 |
+
|
| 142 |
+
### Secure Communication
|
| 143 |
+
- Ed25519 signatures
|
| 144 |
+
- Message authentication
|
| 145 |
+
- Session management
|
| 146 |
+
- Trust verification
|
| 147 |
+
|
| 148 |
+
### Interoperability
|
| 149 |
+
- Vendor-agnostic protocol
|
| 150 |
+
- Standardized message format
|
| 151 |
+
- Capability-based routing
|
| 152 |
+
- Cross-framework communication
|
| 153 |
+
|
| 154 |
+
## Use Cases
|
| 155 |
+
|
| 156 |
+
1. **Multi-Agent Workflows** - Agents from different vendors collaborate on complex tasks
|
| 157 |
+
2. **Skill Marketplace** - Discover and delegate to specialized agents
|
| 158 |
+
3. **Trust Networks** - Build reputation across agent interactions
|
| 159 |
+
4. **Economic Coordination** - Fair billing and micropayments between agents
|
| 160 |
+
5. **Memory Sharing** - Efficient context exchange without duplication
|
| 161 |
+
|
| 162 |
+
## Technical Stack
|
| 163 |
+
|
| 164 |
+
- **Protocol:** JSON-based message format
|
| 165 |
+
- **Cryptography:** Ed25519 (EdDSA)
|
| 166 |
+
- **Python SDK:** Pydantic, cryptography, httpx
|
| 167 |
+
- **Schemas:** JSON Schema Draft 2020-12
|
| 168 |
+
|
| 169 |
+
## Documentation
|
| 170 |
+
|
| 171 |
+
- [Phase 1 Complete](./PHASE1_COMPLETE.md) - Detailed Phase 1 documentation
|
| 172 |
+
- [SDK README](./sdk-python/README.md) - Python SDK documentation
|
| 173 |
+
- [Schemas](./schemas/) - JSON schema specifications
|
| 174 |
+
|
| 175 |
+
## Examples
|
| 176 |
+
|
| 177 |
+
- [basic_handshake.py](./examples/basic_handshake.py) - Complete handshake demo
|
| 178 |
+
- [verify_phase1.py](./examples/verify_phase1.py) - Verification script
|
| 179 |
+
|
| 180 |
+
## Contributing
|
| 181 |
+
|
| 182 |
+
We welcome contributions! Areas of focus:
|
| 183 |
+
|
| 184 |
+
- Protocol design and specification
|
| 185 |
+
- SDK implementations (Python, JS, Rust, Go)
|
| 186 |
+
- Example applications
|
| 187 |
+
- Documentation and tutorials
|
| 188 |
+
- Test coverage
|
| 189 |
+
- Benchmark datasets
|
| 190 |
+
|
| 191 |
+
## Roadmap
|
| 192 |
+
|
| 193 |
+
**Q3 2026**
|
| 194 |
+
- ✅ Phase 1: Core protocol and SDK
|
| 195 |
+
- 🚧 Phase 2: Task negotiation and transport
|
| 196 |
+
|
| 197 |
+
**Q4 2026**
|
| 198 |
+
- Phase 3: Advanced features (memory, trust, economic)
|
| 199 |
+
- Additional language SDKs
|
| 200 |
+
|
| 201 |
+
**2027**
|
| 202 |
+
- Fine-tuned AIPM models
|
| 203 |
+
- Public agent registry
|
| 204 |
+
- Enterprise features
|
| 205 |
+
- Ecosystem growth
|
| 206 |
+
|
| 207 |
+
## License
|
| 208 |
+
|
| 209 |
+
Apache 2.0
|
| 210 |
+
|
| 211 |
+
## Contact
|
| 212 |
+
|
| 213 |
+
- GitHub: https://github.com/aipm/aipm
|
| 214 |
+
- Documentation: https://docs.aipm.org
|
| 215 |
+
- Community: https://discord.gg/aipm
|
| 216 |
+
|
| 217 |
+
---
|
| 218 |
+
|
| 219 |
+
**Building the future of interoperable AI agents** 🚀
|
examples/basic_handshake.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Basic AIPM Handshake Example
|
| 4 |
+
|
| 5 |
+
Demonstrates two AI agents (OpenAI-based and LangGraph-based) performing
|
| 6 |
+
a complete handshake using the AIPM protocol.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import json
|
| 10 |
+
from aipm import (
|
| 11 |
+
AIPMAgent,
|
| 12 |
+
AgentIdentity,
|
| 13 |
+
Capabilities,
|
| 14 |
+
TrustScore,
|
| 15 |
+
MessageType,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def print_message(label: str, message):
|
| 20 |
+
"""Pretty print message"""
|
| 21 |
+
print(f"\n{'='*60}")
|
| 22 |
+
print(f" {label}")
|
| 23 |
+
print(f"{'='*60}")
|
| 24 |
+
print(f"Type: {message.type.value}")
|
| 25 |
+
print(f"From: {message.sender.agent_id}")
|
| 26 |
+
print(f"To: {message.receiver.agent_id}")
|
| 27 |
+
print(f"Payload: {json.dumps(message.payload, indent=2)}")
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def main():
|
| 31 |
+
"""Run basic handshake example"""
|
| 32 |
+
|
| 33 |
+
print("\n" + "="*60)
|
| 34 |
+
print(" AIPM BASIC HANDSHAKE EXAMPLE")
|
| 35 |
+
print("="*60)
|
| 36 |
+
|
| 37 |
+
# Create OpenAI-based agent
|
| 38 |
+
print("\n[1] Creating OpenAI-based Agent...")
|
| 39 |
+
openai_identity = AgentIdentity(
|
| 40 |
+
agent_id="agent-openai-001",
|
| 41 |
+
organization_id="openai",
|
| 42 |
+
name="OpenAI Assistant",
|
| 43 |
+
version="1.0.0",
|
| 44 |
+
capabilities=Capabilities(
|
| 45 |
+
skills=["text-generation", "code-review", "summarization"],
|
| 46 |
+
models=["gpt-4", "gpt-3.5-turbo"],
|
| 47 |
+
tools=["code-interpreter", "web-browser"],
|
| 48 |
+
max_context=128000,
|
| 49 |
+
memory_support=True,
|
| 50 |
+
languages=["en", "es", "fr", "de"],
|
| 51 |
+
),
|
| 52 |
+
trust_score=TrustScore(
|
| 53 |
+
reliability=0.99,
|
| 54 |
+
accuracy=0.95,
|
| 55 |
+
avg_latency_ms=250.0,
|
| 56 |
+
success_rate=0.98,
|
| 57 |
+
total_interactions=10000,
|
| 58 |
+
),
|
| 59 |
+
)
|
| 60 |
+
openai_agent = AIPMAgent(openai_identity)
|
| 61 |
+
print(f"✓ Created {openai_agent}")
|
| 62 |
+
|
| 63 |
+
# Create LangGraph-based agent
|
| 64 |
+
print("\n[2] Creating LangGraph-based Agent...")
|
| 65 |
+
langgraph_identity = AgentIdentity(
|
| 66 |
+
agent_id="agent-langgraph-001",
|
| 67 |
+
organization_id="langchain",
|
| 68 |
+
name="LangGraph Orchestrator",
|
| 69 |
+
version="1.0.0",
|
| 70 |
+
capabilities=Capabilities(
|
| 71 |
+
skills=["workflow-orchestration", "multi-agent-coordination", "data-processing"],
|
| 72 |
+
models=["claude-3-opus", "claude-3-sonnet"],
|
| 73 |
+
tools=["database", "api-caller", "document-processor"],
|
| 74 |
+
max_context=200000,
|
| 75 |
+
memory_support=True,
|
| 76 |
+
languages=["en", "zh", "ja"],
|
| 77 |
+
),
|
| 78 |
+
trust_score=TrustScore(
|
| 79 |
+
reliability=0.97,
|
| 80 |
+
accuracy=0.93,
|
| 81 |
+
avg_latency_ms=300.0,
|
| 82 |
+
success_rate=0.96,
|
| 83 |
+
total_interactions=5000,
|
| 84 |
+
),
|
| 85 |
+
)
|
| 86 |
+
langgraph_agent = AIPMAgent(langgraph_identity)
|
| 87 |
+
print(f"✓ Created {langgraph_agent}")
|
| 88 |
+
|
| 89 |
+
# Start handshake
|
| 90 |
+
print("\n" + "="*60)
|
| 91 |
+
print(" HANDSHAKE PROTOCOL")
|
| 92 |
+
print("="*60)
|
| 93 |
+
|
| 94 |
+
# Step 1: OpenAI agent initiates with HELLO
|
| 95 |
+
print("\n[Step 1] OpenAI Agent → LangGraph Agent: HELLO")
|
| 96 |
+
hello_msg = openai_agent.initiate_handshake(langgraph_identity.to_reference())
|
| 97 |
+
print_message("HELLO Message", hello_msg)
|
| 98 |
+
|
| 99 |
+
# Step 2: LangGraph agent responds with CAPABILITY_EXCHANGE
|
| 100 |
+
print("\n[Step 2] LangGraph Agent → OpenAI Agent: CAPABILITY_EXCHANGE")
|
| 101 |
+
capability_msg = langgraph_agent.process_message(hello_msg)
|
| 102 |
+
print_message("CAPABILITY_EXCHANGE Message", capability_msg)
|
| 103 |
+
|
| 104 |
+
# Step 3: OpenAI agent continues with AUTHENTICATION
|
| 105 |
+
print("\n[Step 3] OpenAI Agent → LangGraph Agent: AUTHENTICATION")
|
| 106 |
+
auth_msg = openai_agent.process_message(capability_msg)
|
| 107 |
+
print_message("AUTHENTICATION Message", auth_msg)
|
| 108 |
+
|
| 109 |
+
# Step 4: LangGraph agent exchanges PUBLIC_KEY
|
| 110 |
+
print("\n[Step 4] LangGraph Agent → OpenAI Agent: PUBLIC_KEY_EXCHANGE")
|
| 111 |
+
key_msg = langgraph_agent.process_message(auth_msg)
|
| 112 |
+
print_message("PUBLIC_KEY_EXCHANGE Message", key_msg)
|
| 113 |
+
|
| 114 |
+
# Step 5: OpenAI agent verifies TRUST
|
| 115 |
+
print("\n[Step 5] OpenAI Agent → LangGraph Agent: TRUST_VERIFICATION")
|
| 116 |
+
trust_msg = openai_agent.process_message(key_msg)
|
| 117 |
+
print_message("TRUST_VERIFICATION Message", trust_msg)
|
| 118 |
+
|
| 119 |
+
# Step 6: LangGraph agent sends READY
|
| 120 |
+
print("\n[Step 6] LangGraph Agent → OpenAI Agent: READY")
|
| 121 |
+
ready_msg = langgraph_agent.process_message(trust_msg)
|
| 122 |
+
print_message("READY Message", ready_msg)
|
| 123 |
+
|
| 124 |
+
# Step 7: OpenAI agent confirms READY
|
| 125 |
+
print("\n[Step 7] OpenAI Agent confirms READY")
|
| 126 |
+
openai_agent.process_message(ready_msg)
|
| 127 |
+
|
| 128 |
+
# Verify handshake completion
|
| 129 |
+
print("\n" + "="*60)
|
| 130 |
+
print(" HANDSHAKE COMPLETE")
|
| 131 |
+
print("="*60)
|
| 132 |
+
|
| 133 |
+
openai_ready = openai_agent.is_ready(langgraph_identity.to_reference())
|
| 134 |
+
langgraph_ready = langgraph_agent.is_ready(openai_identity.to_reference())
|
| 135 |
+
|
| 136 |
+
print(f"\nOpenAI Agent Ready: {openai_ready} ✓")
|
| 137 |
+
print(f"LangGraph Agent Ready: {langgraph_ready} ✓")
|
| 138 |
+
|
| 139 |
+
# Exchange identity information
|
| 140 |
+
print("\n" + "="*60)
|
| 141 |
+
print(" PEER IDENTITY EXCHANGE")
|
| 142 |
+
print("="*60)
|
| 143 |
+
|
| 144 |
+
openai_peer = openai_agent.get_peer_identity(langgraph_identity.to_reference())
|
| 145 |
+
langgraph_peer = langgraph_agent.get_peer_identity(openai_identity.to_reference())
|
| 146 |
+
|
| 147 |
+
print(f"\nOpenAI knows LangGraph as:")
|
| 148 |
+
print(f" - Name: {openai_peer.name}")
|
| 149 |
+
print(f" - Skills: {', '.join(openai_peer.capabilities.skills)}")
|
| 150 |
+
print(f" - Models: {', '.join(openai_peer.capabilities.models)}")
|
| 151 |
+
print(f" - Trust Score: {openai_peer.trust_score.reliability:.2f}")
|
| 152 |
+
|
| 153 |
+
print(f"\nLangGraph knows OpenAI as:")
|
| 154 |
+
print(f" - Name: {langgraph_peer.name}")
|
| 155 |
+
print(f" - Skills: {', '.join(langgraph_peer.capabilities.skills)}")
|
| 156 |
+
print(f" - Models: {', '.join(langgraph_peer.capabilities.models)}")
|
| 157 |
+
print(f" - Trust Score: {langgraph_peer.trust_score.reliability:.2f}")
|
| 158 |
+
|
| 159 |
+
# Create a task request
|
| 160 |
+
print("\n" + "="*60)
|
| 161 |
+
print(" TASK REQUEST EXAMPLE")
|
| 162 |
+
print("="*60)
|
| 163 |
+
|
| 164 |
+
task_msg = openai_agent.create_task_request(
|
| 165 |
+
langgraph_identity.to_reference(),
|
| 166 |
+
task_description="Process customer feedback data and generate insights",
|
| 167 |
+
priority="high",
|
| 168 |
+
dataset_size=1000,
|
| 169 |
+
deadline="2026-07-10T00:00:00Z",
|
| 170 |
+
)
|
| 171 |
+
print_message("Task Request from OpenAI to LangGraph", task_msg)
|
| 172 |
+
|
| 173 |
+
print("\n" + "="*60)
|
| 174 |
+
print(" ✓ EXAMPLE COMPLETE")
|
| 175 |
+
print("="*60)
|
| 176 |
+
print("\nTwo agents from different vendors successfully:")
|
| 177 |
+
print(" 1. Completed secure handshake")
|
| 178 |
+
print(" 2. Exchanged capabilities")
|
| 179 |
+
print(" 3. Verified trust")
|
| 180 |
+
print(" 4. Ready for task delegation")
|
| 181 |
+
print("\nThis is the foundation for cross-vendor agent interoperability! 🚀")
|
| 182 |
+
print()
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
if __name__ == "__main__":
|
| 186 |
+
main()
|
examples/verify_phase1.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
AIPM Phase 1 Verification Script
|
| 4 |
+
|
| 5 |
+
Verifies that all Phase 1 components are properly implemented:
|
| 6 |
+
1. JSON schemas defined
|
| 7 |
+
2. Python SDK scaffolded
|
| 8 |
+
3. Identity & handshake models implemented
|
| 9 |
+
4. Basic agent implementation complete
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import sys
|
| 13 |
+
import json
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
|
| 16 |
+
def verify_schemas():
|
| 17 |
+
"""Verify JSON schemas exist and are valid"""
|
| 18 |
+
print("\n[1] Verifying JSON Schemas...")
|
| 19 |
+
|
| 20 |
+
schemas_dir = Path(__file__).parent.parent / "schemas"
|
| 21 |
+
|
| 22 |
+
# Check agent_identity schema
|
| 23 |
+
identity_schema = schemas_dir / "agent_identity.schema"
|
| 24 |
+
if not identity_schema.exists():
|
| 25 |
+
print("❌ agent_identity.schema not found")
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
with open(identity_schema) as f:
|
| 29 |
+
identity_data = json.load(f)
|
| 30 |
+
print(f"✓ agent_identity.schema found ({len(identity_data)} keys)")
|
| 31 |
+
|
| 32 |
+
# Check message_envelope schema
|
| 33 |
+
envelope_schema = schemas_dir / "message_envelope.schema"
|
| 34 |
+
if not envelope_schema.exists():
|
| 35 |
+
print("❌ message_envelope.schema not found")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
with open(envelope_schema) as f:
|
| 39 |
+
envelope_data = json.load(f)
|
| 40 |
+
print(f"✓ message_envelope.schema found ({len(envelope_data)} keys)")
|
| 41 |
+
|
| 42 |
+
return True
|
| 43 |
+
|
| 44 |
+
def verify_sdk_structure():
|
| 45 |
+
"""Verify SDK folder structure"""
|
| 46 |
+
print("\n[2] Verifying SDK Structure...")
|
| 47 |
+
|
| 48 |
+
sdk_dir = Path(__file__).parent.parent / "sdk-python" / "aipm"
|
| 49 |
+
|
| 50 |
+
required_files = [
|
| 51 |
+
"__init__.py",
|
| 52 |
+
"models.py",
|
| 53 |
+
"handshake.py",
|
| 54 |
+
"agent.py",
|
| 55 |
+
"crypto.py",
|
| 56 |
+
"exceptions.py",
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
for filename in required_files:
|
| 60 |
+
filepath = sdk_dir / filename
|
| 61 |
+
if not filepath.exists():
|
| 62 |
+
print(f"❌ {filename} not found")
|
| 63 |
+
return False
|
| 64 |
+
print(f"✓ {filename} found ({filepath.stat().st_size} bytes)")
|
| 65 |
+
|
| 66 |
+
return True
|
| 67 |
+
|
| 68 |
+
def verify_imports():
|
| 69 |
+
"""Verify Python imports work (without dependencies)"""
|
| 70 |
+
print("\n[3] Verifying Module Structure...")
|
| 71 |
+
|
| 72 |
+
# Add SDK to path
|
| 73 |
+
from pathlib import Path as PathLib
|
| 74 |
+
sdk_path = PathLib(__file__).parent.parent / "sdk-python"
|
| 75 |
+
sys.path.insert(0, str(sdk_path))
|
| 76 |
+
|
| 77 |
+
try:
|
| 78 |
+
# Just verify the module structure, don't import pydantic
|
| 79 |
+
import importlib.util
|
| 80 |
+
|
| 81 |
+
# Check if models module exists
|
| 82 |
+
models_path = sdk_path / "aipm" / "models.py"
|
| 83 |
+
spec = importlib.util.spec_from_file_location("aipm.models", models_path)
|
| 84 |
+
print(f"✓ models.py is importable")
|
| 85 |
+
|
| 86 |
+
# Check handshake
|
| 87 |
+
handshake_path = sdk_path / "aipm" / "handshake.py"
|
| 88 |
+
spec = importlib.util.spec_from_file_location("aipm.handshake", handshake_path)
|
| 89 |
+
print(f"✓ handshake.py is importable")
|
| 90 |
+
|
| 91 |
+
# Check agent
|
| 92 |
+
agent_path = sdk_path / "aipm" / "agent.py"
|
| 93 |
+
spec = importlib.util.spec_from_file_location("aipm.agent", agent_path)
|
| 94 |
+
print(f"✓ agent.py is importable")
|
| 95 |
+
|
| 96 |
+
return True
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f"❌ Import error: {e}")
|
| 99 |
+
return False
|
| 100 |
+
|
| 101 |
+
def verify_example():
|
| 102 |
+
"""Verify example script exists"""
|
| 103 |
+
print("\n[4] Verifying Example Script...")
|
| 104 |
+
|
| 105 |
+
example_path = Path(__file__).parent / "basic_handshake.py"
|
| 106 |
+
if not example_path.exists():
|
| 107 |
+
print("❌ basic_handshake.py not found")
|
| 108 |
+
return False
|
| 109 |
+
|
| 110 |
+
with open(example_path) as f:
|
| 111 |
+
lines = len(f.readlines())
|
| 112 |
+
print(f"✓ basic_handshake.py found ({lines} lines)")
|
| 113 |
+
|
| 114 |
+
return True
|
| 115 |
+
|
| 116 |
+
def main():
|
| 117 |
+
print("="*60)
|
| 118 |
+
print(" AIPM PHASE 1 VERIFICATION")
|
| 119 |
+
print("="*60)
|
| 120 |
+
|
| 121 |
+
results = {
|
| 122 |
+
"Schemas": verify_schemas(),
|
| 123 |
+
"SDK Structure": verify_sdk_structure(),
|
| 124 |
+
"Imports": verify_imports(),
|
| 125 |
+
"Example": verify_example(),
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
print("\n" + "="*60)
|
| 129 |
+
print(" VERIFICATION RESULTS")
|
| 130 |
+
print("="*60)
|
| 131 |
+
|
| 132 |
+
for component, passed in results.items():
|
| 133 |
+
status = "✓ PASS" if passed else "❌ FAIL"
|
| 134 |
+
print(f"{component:.<40} {status}")
|
| 135 |
+
|
| 136 |
+
all_passed = all(results.values())
|
| 137 |
+
|
| 138 |
+
print("\n" + "="*60)
|
| 139 |
+
if all_passed:
|
| 140 |
+
print(" ✓ PHASE 1 COMPLETE")
|
| 141 |
+
print("="*60)
|
| 142 |
+
print("\nAll Phase 1 components verified:")
|
| 143 |
+
print(" 1. ✓ JSON schemas defined")
|
| 144 |
+
print(" 2. ✓ Python SDK scaffolded")
|
| 145 |
+
print(" 3. ✓ Identity & handshake models implemented")
|
| 146 |
+
print(" 4. ✓ Basic agent implementation complete")
|
| 147 |
+
print("\nReady to proceed to Phase 2:")
|
| 148 |
+
print(" - Task Negotiation Framework")
|
| 149 |
+
print(" - Cryptographic Message Signing")
|
| 150 |
+
print(" - HTTP/WebSocket Transport Layer")
|
| 151 |
+
print("\nTo run the full example (requires dependencies):")
|
| 152 |
+
print(" cd sdk-python && pip install -e .")
|
| 153 |
+
print(" python examples/basic_handshake.py")
|
| 154 |
+
else:
|
| 155 |
+
print(" ❌ PHASE 1 INCOMPLETE")
|
| 156 |
+
print("="*60)
|
| 157 |
+
print("\nSome components failed verification.")
|
| 158 |
+
|
| 159 |
+
print()
|
| 160 |
+
return 0 if all_passed else 1
|
| 161 |
+
|
| 162 |
+
if __name__ == "__main__":
|
| 163 |
+
sys.exit(main())
|
schemas/agent_identity.schema
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
| 3 |
+
"$id": "https://aipm.org/schemas/identity/v1.0.0",
|
| 4 |
+
"title": "AIPM Agent Identity",
|
| 5 |
+
"description": "Agent identity and capability declaration",
|
| 6 |
+
"type": "object",
|
| 7 |
+
"required": ["agent_id", "organization_id", "name", "version", "capabilities"],
|
| 8 |
+
"properties": {
|
| 9 |
+
"agent_id": {
|
| 10 |
+
"type": "string",
|
| 11 |
+
"description": "Unique agent identifier"
|
| 12 |
+
},
|
| 13 |
+
"organization_id": {
|
| 14 |
+
"type": "string",
|
| 15 |
+
"description": "Organization or vendor identifier"
|
| 16 |
+
},
|
| 17 |
+
"name": {
|
| 18 |
+
"type": "string",
|
| 19 |
+
"description": "Human-readable agent name"
|
| 20 |
+
},
|
| 21 |
+
"version": {
|
| 22 |
+
"type": "string",
|
| 23 |
+
"description": "Agent version"
|
| 24 |
+
},
|
| 25 |
+
"public_key": {
|
| 26 |
+
"type": "string",
|
| 27 |
+
"description": "Base64-encoded public key for encryption"
|
| 28 |
+
},
|
| 29 |
+
"capabilities": {
|
| 30 |
+
"type": "object",
|
| 31 |
+
"properties": {
|
| 32 |
+
"skills": {
|
| 33 |
+
"type": "array",
|
| 34 |
+
"items": {"type": "string"},
|
| 35 |
+
"description": "List of supported skills"
|
| 36 |
+
},
|
| 37 |
+
"models": {
|
| 38 |
+
"type": "array",
|
| 39 |
+
"items": {"type": "string"},
|
| 40 |
+
"description": "AI models available"
|
| 41 |
+
},
|
| 42 |
+
"tools": {
|
| 43 |
+
"type": "array",
|
| 44 |
+
"items": {"type": "string"},
|
| 45 |
+
"description": "Available tools and APIs"
|
| 46 |
+
},
|
| 47 |
+
"max_context": {
|
| 48 |
+
"type": "integer",
|
| 49 |
+
"description": "Maximum context window size"
|
| 50 |
+
},
|
| 51 |
+
"memory_support": {
|
| 52 |
+
"type": "boolean",
|
| 53 |
+
"description": "Supports memory exchange"
|
| 54 |
+
},
|
| 55 |
+
"languages": {
|
| 56 |
+
"type": "array",
|
| 57 |
+
"items": {"type": "string"},
|
| 58 |
+
"description": "Supported languages"
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
},
|
| 62 |
+
"trust_score": {
|
| 63 |
+
"type": "object",
|
| 64 |
+
"properties": {
|
| 65 |
+
"reliability": {"type": "number", "minimum": 0, "maximum": 1},
|
| 66 |
+
"accuracy": {"type": "number", "minimum": 0, "maximum": 1},
|
| 67 |
+
"avg_latency_ms": {"type": "number"},
|
| 68 |
+
"success_rate": {"type": "number", "minimum": 0, "maximum": 1},
|
| 69 |
+
"total_interactions": {"type": "integer"}
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"economic": {
|
| 73 |
+
"type": "object",
|
| 74 |
+
"properties": {
|
| 75 |
+
"cost_per_request": {"type": "number"},
|
| 76 |
+
"currency": {"type": "string"},
|
| 77 |
+
"billing_model": {"type": "string", "enum": ["per_request", "per_token", "subscription"]}
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
}
|
schemas/message_envelope.schema
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
| 3 |
+
"$id": "https://aipm.org/schemas/message/v1.0.0",
|
| 4 |
+
"title": "AIPM Message Envelope",
|
| 5 |
+
"description": "Standard message format for Agent Interoperability Protocol",
|
| 6 |
+
"type": "object",
|
| 7 |
+
"required": ["id", "type", "version", "sender", "receiver", "timestamp", "payload"],
|
| 8 |
+
"properties": {
|
| 9 |
+
"id": {
|
| 10 |
+
"type": "string",
|
| 11 |
+
"description": "Unique message identifier (UUID v4)"
|
| 12 |
+
},
|
| 13 |
+
"type": {
|
| 14 |
+
"type": "string",
|
| 15 |
+
"description": "Message type identifier",
|
| 16 |
+
"enum": [
|
| 17 |
+
"handshake.hello",
|
| 18 |
+
"handshake.capability_exchange",
|
| 19 |
+
"handshake.authentication",
|
| 20 |
+
"handshake.public_key_exchange",
|
| 21 |
+
"handshake.trust_verification",
|
| 22 |
+
"handshake.ready",
|
| 23 |
+
"task.request",
|
| 24 |
+
"task.accept",
|
| 25 |
+
"task.decline",
|
| 26 |
+
"memory.share",
|
| 27 |
+
"error"
|
| 28 |
+
]
|
| 29 |
+
},
|
| 30 |
+
"version": {
|
| 31 |
+
"type": "string",
|
| 32 |
+
"description": "Protocol version",
|
| 33 |
+
"default": "1.0.0"
|
| 34 |
+
},
|
| 35 |
+
"sender": {
|
| 36 |
+
"type": "object",
|
| 37 |
+
"required": ["agent_id", "organization_id"],
|
| 38 |
+
"properties": {
|
| 39 |
+
"agent_id": {"type": "string"},
|
| 40 |
+
"organization_id": {"type": "string"}
|
| 41 |
+
}
|
| 42 |
+
},
|
| 43 |
+
"receiver": {
|
| 44 |
+
"type": "object",
|
| 45 |
+
"required": ["agent_id", "organization_id"],
|
| 46 |
+
"properties": {
|
| 47 |
+
"agent_id": {"type": "string"},
|
| 48 |
+
"organization_id": {"type": "string"}
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
"timestamp": {
|
| 52 |
+
"type": "string",
|
| 53 |
+
"format": "date-time"
|
| 54 |
+
},
|
| 55 |
+
"priority": {
|
| 56 |
+
"type": "string",
|
| 57 |
+
"enum": ["low", "normal", "high", "critical"],
|
| 58 |
+
"default": "normal"
|
| 59 |
+
},
|
| 60 |
+
"payload": {
|
| 61 |
+
"type": "object"
|
| 62 |
+
},
|
| 63 |
+
"signature": {
|
| 64 |
+
"type": "object",
|
| 65 |
+
"properties": {
|
| 66 |
+
"algorithm": {"type": "string"},
|
| 67 |
+
"value": {"type": "string"},
|
| 68 |
+
"public_key_fingerprint": {"type": "string"}
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
}
|
sdk-python/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AIPM Python SDK
|
| 2 |
+
|
| 3 |
+
**Agent Interoperability Protocol Models** - Python reference implementation
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
AIPM is the first open ecosystem for interoperable AI agents, enabling agents from different vendors (OpenAI, Claude, LangGraph, AutoGen, CrewAI, etc.) to communicate securely and consistently using the same protocol.
|
| 8 |
+
|
| 9 |
+
## Features
|
| 10 |
+
|
| 11 |
+
✅ **Identity Layer** - Standardized agent identification and capabilities
|
| 12 |
+
✅ **Secure Handshake** - TLS-inspired connection establishment
|
| 13 |
+
✅ **Capability Discovery** - Automatic discovery of skills and tools
|
| 14 |
+
✅ **Task Negotiation** - Accept/decline work based on capability
|
| 15 |
+
✅ **Memory Exchange** - Efficient context sharing
|
| 16 |
+
✅ **Trust Layer** - Reputation and reliability tracking
|
| 17 |
+
✅ **Cryptographic Security** - Ed25519 signatures and verification
|
| 18 |
+
|
| 19 |
+
## Installation
|
| 20 |
+
|
| 21 |
+
```bash
|
| 22 |
+
pip install aipm
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Or from source:
|
| 26 |
+
|
| 27 |
+
```bash
|
| 28 |
+
git clone https://github.com/aipm/aipm.git
|
| 29 |
+
cd aipm/sdk-python
|
| 30 |
+
pip install -e .
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
## Quick Start
|
| 34 |
+
|
| 35 |
+
```python
|
| 36 |
+
from aipm import AIPMAgent, AgentIdentity, AgentReference, Capabilities
|
| 37 |
+
|
| 38 |
+
# Create agent identity
|
| 39 |
+
identity = AgentIdentity(
|
| 40 |
+
agent_id="my-agent-001",
|
| 41 |
+
organization_id="my-org",
|
| 42 |
+
name="My AI Agent",
|
| 43 |
+
version="1.0.0",
|
| 44 |
+
capabilities=Capabilities(
|
| 45 |
+
skills=["text-generation", "code-review"],
|
| 46 |
+
models=["gpt-4"],
|
| 47 |
+
tools=["code-interpreter"],
|
| 48 |
+
)
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
# Initialize agent
|
| 52 |
+
agent = AIPMAgent(identity)
|
| 53 |
+
|
| 54 |
+
# Initiate handshake with another agent
|
| 55 |
+
peer = AgentReference(
|
| 56 |
+
agent_id="peer-agent-001",
|
| 57 |
+
organization_id="peer-org"
|
| 58 |
+
)
|
| 59 |
+
hello_msg = agent.initiate_handshake(peer)
|
| 60 |
+
|
| 61 |
+
# Process messages
|
| 62 |
+
response = agent.process_message(hello_msg)
|
| 63 |
+
|
| 64 |
+
# Check if ready
|
| 65 |
+
if agent.is_ready(peer):
|
| 66 |
+
# Send task request
|
| 67 |
+
task_msg = agent.create_task_request(
|
| 68 |
+
peer,
|
| 69 |
+
task_description="Process customer data",
|
| 70 |
+
priority="high"
|
| 71 |
+
)
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
## Examples
|
| 75 |
+
|
| 76 |
+
See `examples/basic_handshake.py` for a complete handshake between two agents:
|
| 77 |
+
|
| 78 |
+
```bash
|
| 79 |
+
cd examples
|
| 80 |
+
python basic_handshake.py
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
## Architecture
|
| 84 |
+
|
| 85 |
+
### Core Components
|
| 86 |
+
|
| 87 |
+
- **AgentIdentity** - Complete agent profile with capabilities
|
| 88 |
+
- **MessageEnvelope** - Standard message format
|
| 89 |
+
- **HandshakeManager** - State machine for secure handshake
|
| 90 |
+
- **AIPMAgent** - Base agent implementation
|
| 91 |
+
- **CryptoManager** - Cryptographic operations
|
| 92 |
+
|
| 93 |
+
### Handshake Protocol
|
| 94 |
+
|
| 95 |
+
```
|
| 96 |
+
Agent A Agent B
|
| 97 |
+
| |
|
| 98 |
+
|------- HELLO ----------------->|
|
| 99 |
+
|<--- CAPABILITY_EXCHANGE -------|
|
| 100 |
+
|------- AUTHENTICATION -------->|
|
| 101 |
+
|<--- PUBLIC_KEY_EXCHANGE -------|
|
| 102 |
+
|------- TRUST_VERIFICATION ---->|
|
| 103 |
+
|<--- READY ---------------------|
|
| 104 |
+
| |
|
| 105 |
+
[Ready for task delegation]
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
## API Reference
|
| 109 |
+
|
| 110 |
+
### AgentIdentity
|
| 111 |
+
|
| 112 |
+
```python
|
| 113 |
+
identity = AgentIdentity(
|
| 114 |
+
agent_id="unique-id",
|
| 115 |
+
organization_id="org-id",
|
| 116 |
+
name="Agent Name",
|
| 117 |
+
version="1.0.0",
|
| 118 |
+
capabilities=Capabilities(...),
|
| 119 |
+
trust_score=TrustScore(...),
|
| 120 |
+
)
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
### AIPMAgent
|
| 124 |
+
|
| 125 |
+
```python
|
| 126 |
+
agent = AIPMAgent(identity)
|
| 127 |
+
|
| 128 |
+
# Initiate handshake
|
| 129 |
+
msg = agent.initiate_handshake(peer_reference)
|
| 130 |
+
|
| 131 |
+
# Process incoming messages
|
| 132 |
+
response = agent.process_message(incoming_msg)
|
| 133 |
+
|
| 134 |
+
# Check handshake status
|
| 135 |
+
is_ready = agent.is_ready(peer_reference)
|
| 136 |
+
|
| 137 |
+
# Get peer identity
|
| 138 |
+
peer_identity = agent.get_peer_identity(peer_reference)
|
| 139 |
+
|
| 140 |
+
# Create task request
|
| 141 |
+
task_msg = agent.create_task_request(
|
| 142 |
+
peer_reference,
|
| 143 |
+
task_description="Process data",
|
| 144 |
+
priority="high"
|
| 145 |
+
)
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
## Development
|
| 149 |
+
|
| 150 |
+
Install development dependencies:
|
| 151 |
+
|
| 152 |
+
```bash
|
| 153 |
+
pip install -e ".[dev]"
|
| 154 |
+
```
|
| 155 |
+
|
| 156 |
+
Run tests:
|
| 157 |
+
|
| 158 |
+
```bash
|
| 159 |
+
pytest
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
Format code:
|
| 163 |
+
|
| 164 |
+
```bash
|
| 165 |
+
black aipm/
|
| 166 |
+
ruff check aipm/
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
## Roadmap
|
| 170 |
+
|
| 171 |
+
- [x] Core protocol schemas
|
| 172 |
+
- [x] Identity and handshake models
|
| 173 |
+
- [x] Basic agent implementation
|
| 174 |
+
- [ ] Async support
|
| 175 |
+
- [ ] HTTP/WebSocket transport
|
| 176 |
+
- [ ] Task negotiation framework
|
| 177 |
+
- [ ] Memory exchange protocol
|
| 178 |
+
- [ ] Trust scoring system
|
| 179 |
+
- [ ] Economic layer
|
| 180 |
+
- [ ] Comprehensive test suite
|
| 181 |
+
|
| 182 |
+
## Contributing
|
| 183 |
+
|
| 184 |
+
Contributions welcome! Please see CONTRIBUTING.md
|
| 185 |
+
|
| 186 |
+
## License
|
| 187 |
+
|
| 188 |
+
Apache 2.0
|
| 189 |
+
|
| 190 |
+
## Links
|
| 191 |
+
|
| 192 |
+
- Documentation: https://docs.aipm.org
|
| 193 |
+
- Repository: https://github.com/aipm/aipm
|
| 194 |
+
- Issues: https://github.com/aipm/aipm/issues
|
sdk-python/aipm/__init__.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
AIPM - Agent Interoperability Protocol Models
|
| 3 |
+
Python SDK for building interoperable AI agents
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from aipm.models import (
|
| 7 |
+
AgentIdentity,
|
| 8 |
+
AgentReference,
|
| 9 |
+
Capabilities,
|
| 10 |
+
TrustScore,
|
| 11 |
+
EconomicInfo,
|
| 12 |
+
MessageEnvelope,
|
| 13 |
+
MessageType,
|
| 14 |
+
Priority,
|
| 15 |
+
)
|
| 16 |
+
from aipm.handshake import HandshakeState, HandshakeManager
|
| 17 |
+
from aipm.agent import AIPMAgent
|
| 18 |
+
from aipm.crypto import CryptoManager
|
| 19 |
+
from aipm.exceptions import (
|
| 20 |
+
AIPMException,
|
| 21 |
+
HandshakeError,
|
| 22 |
+
ValidationError,
|
| 23 |
+
AuthenticationError,
|
| 24 |
+
EncryptionError,
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
__version__ = "0.1.0"
|
| 28 |
+
|
| 29 |
+
__all__ = [
|
| 30 |
+
# Models
|
| 31 |
+
"AgentIdentity",
|
| 32 |
+
"AgentReference",
|
| 33 |
+
"Capabilities",
|
| 34 |
+
"TrustScore",
|
| 35 |
+
"EconomicInfo",
|
| 36 |
+
"MessageEnvelope",
|
| 37 |
+
"MessageType",
|
| 38 |
+
"Priority",
|
| 39 |
+
# Handshake
|
| 40 |
+
"HandshakeState",
|
| 41 |
+
"HandshakeManager",
|
| 42 |
+
# Agent
|
| 43 |
+
"AIPMAgent",
|
| 44 |
+
# Crypto
|
| 45 |
+
"CryptoManager",
|
| 46 |
+
# Exceptions
|
| 47 |
+
"AIPMException",
|
| 48 |
+
"HandshakeError",
|
| 49 |
+
"ValidationError",
|
| 50 |
+
"AuthenticationError",
|
| 51 |
+
"EncryptionError",
|
| 52 |
+
]
|
sdk-python/aipm/__pycache__/__init__.cpython-314.pyc
ADDED
|
Binary file (1.01 kB). View file
|
|
|
sdk-python/aipm/__pycache__/models.cpython-314.pyc
ADDED
|
Binary file (12.7 kB). View file
|
|
|
sdk-python/aipm/agent.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Base AIPM Agent implementation
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
from typing import Callable, Dict, Optional
|
| 7 |
+
|
| 8 |
+
from aipm.models import AgentIdentity, AgentReference, MessageEnvelope, MessageType
|
| 9 |
+
from aipm.handshake import HandshakeManager, HandshakeState
|
| 10 |
+
from aipm.crypto import CryptoManager
|
| 11 |
+
from aipm.exceptions import AIPMException, HandshakeError
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class AIPMAgent:
|
| 15 |
+
"""
|
| 16 |
+
Base AIPM Agent implementation with handshake protocol support
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self, identity: AgentIdentity, auto_generate_keys: bool = True):
|
| 20 |
+
"""
|
| 21 |
+
Initialize AIPM Agent
|
| 22 |
+
|
| 23 |
+
Args:
|
| 24 |
+
identity: Agent identity
|
| 25 |
+
auto_generate_keys: Automatically generate cryptographic keys
|
| 26 |
+
"""
|
| 27 |
+
self.identity = identity
|
| 28 |
+
self.crypto = CryptoManager()
|
| 29 |
+
self.handshake_managers: Dict[str, HandshakeManager] = {}
|
| 30 |
+
self.message_handlers: Dict[MessageType, Callable] = {}
|
| 31 |
+
|
| 32 |
+
# Generate keys if requested and not provided
|
| 33 |
+
if auto_generate_keys and not identity.public_key:
|
| 34 |
+
private_key, public_key = self.crypto.generate_keypair()
|
| 35 |
+
self.identity.public_key = public_key
|
| 36 |
+
|
| 37 |
+
# Register default message handlers
|
| 38 |
+
self._register_default_handlers()
|
| 39 |
+
|
| 40 |
+
def _register_default_handlers(self) -> None:
|
| 41 |
+
"""Register default handlers for handshake messages"""
|
| 42 |
+
self.message_handlers[MessageType.HANDSHAKE_HELLO] = self._handle_hello
|
| 43 |
+
self.message_handlers[
|
| 44 |
+
MessageType.HANDSHAKE_CAPABILITY_EXCHANGE
|
| 45 |
+
] = self._handle_capability_exchange
|
| 46 |
+
self.message_handlers[
|
| 47 |
+
MessageType.HANDSHAKE_AUTHENTICATION
|
| 48 |
+
] = self._handle_authentication
|
| 49 |
+
self.message_handlers[
|
| 50 |
+
MessageType.HANDSHAKE_PUBLIC_KEY_EXCHANGE
|
| 51 |
+
] = self._handle_key_exchange
|
| 52 |
+
self.message_handlers[
|
| 53 |
+
MessageType.HANDSHAKE_TRUST_VERIFICATION
|
| 54 |
+
] = self._handle_trust_verification
|
| 55 |
+
self.message_handlers[MessageType.HANDSHAKE_READY] = self._handle_ready
|
| 56 |
+
|
| 57 |
+
def initiate_handshake(self, peer: AgentReference) -> MessageEnvelope:
|
| 58 |
+
"""
|
| 59 |
+
Initiate handshake with another agent
|
| 60 |
+
|
| 61 |
+
Args:
|
| 62 |
+
peer: Target agent reference
|
| 63 |
+
|
| 64 |
+
Returns:
|
| 65 |
+
Initial HELLO message
|
| 66 |
+
"""
|
| 67 |
+
peer_key = f"{peer.organization_id}/{peer.agent_id}"
|
| 68 |
+
|
| 69 |
+
if peer_key in self.handshake_managers:
|
| 70 |
+
# Reset existing handshake
|
| 71 |
+
self.handshake_managers[peer_key].reset()
|
| 72 |
+
else:
|
| 73 |
+
# Create new handshake manager
|
| 74 |
+
self.handshake_managers[peer_key] = HandshakeManager(self.identity)
|
| 75 |
+
|
| 76 |
+
manager = self.handshake_managers[peer_key]
|
| 77 |
+
return manager.create_hello_message(peer)
|
| 78 |
+
|
| 79 |
+
def process_message(self, message: MessageEnvelope) -> Optional[MessageEnvelope]:
|
| 80 |
+
"""
|
| 81 |
+
Process incoming AIPM message
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
message: Incoming message
|
| 85 |
+
|
| 86 |
+
Returns:
|
| 87 |
+
Response message if applicable
|
| 88 |
+
"""
|
| 89 |
+
# Find appropriate handler
|
| 90 |
+
handler = self.message_handlers.get(message.type)
|
| 91 |
+
if not handler:
|
| 92 |
+
raise AIPMException(f"No handler for message type: {message.type}")
|
| 93 |
+
|
| 94 |
+
return handler(message)
|
| 95 |
+
|
| 96 |
+
def _get_handshake_manager(self, peer: AgentReference) -> HandshakeManager:
|
| 97 |
+
"""Get or create handshake manager for peer"""
|
| 98 |
+
peer_key = f"{peer.organization_id}/{peer.agent_id}"
|
| 99 |
+
|
| 100 |
+
if peer_key not in self.handshake_managers:
|
| 101 |
+
self.handshake_managers[peer_key] = HandshakeManager(self.identity)
|
| 102 |
+
|
| 103 |
+
return self.handshake_managers[peer_key]
|
| 104 |
+
|
| 105 |
+
def _handle_hello(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 106 |
+
"""Handle HELLO message"""
|
| 107 |
+
manager = self._get_handshake_manager(message.sender)
|
| 108 |
+
return manager.handle_hello_message(message)
|
| 109 |
+
|
| 110 |
+
def _handle_capability_exchange(
|
| 111 |
+
self, message: MessageEnvelope
|
| 112 |
+
) -> Optional[MessageEnvelope]:
|
| 113 |
+
"""Handle capability exchange"""
|
| 114 |
+
manager = self._get_handshake_manager(message.sender)
|
| 115 |
+
return manager.handle_capability_exchange(message)
|
| 116 |
+
|
| 117 |
+
def _handle_authentication(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 118 |
+
"""Handle authentication"""
|
| 119 |
+
manager = self._get_handshake_manager(message.sender)
|
| 120 |
+
return manager.handle_authentication(message)
|
| 121 |
+
|
| 122 |
+
def _handle_key_exchange(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 123 |
+
"""Handle key exchange"""
|
| 124 |
+
manager = self._get_handshake_manager(message.sender)
|
| 125 |
+
return manager.handle_key_exchange(message)
|
| 126 |
+
|
| 127 |
+
def _handle_trust_verification(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 128 |
+
"""Handle trust verification"""
|
| 129 |
+
manager = self._get_handshake_manager(message.sender)
|
| 130 |
+
return manager.handle_trust_verification(message)
|
| 131 |
+
|
| 132 |
+
def _handle_ready(self, message: MessageEnvelope) -> None:
|
| 133 |
+
"""Handle READY message"""
|
| 134 |
+
manager = self._get_handshake_manager(message.sender)
|
| 135 |
+
manager.handle_ready_message(message)
|
| 136 |
+
|
| 137 |
+
def is_ready(self, peer: AgentReference) -> bool:
|
| 138 |
+
"""
|
| 139 |
+
Check if handshake with peer is complete
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
peer: Peer agent reference
|
| 143 |
+
|
| 144 |
+
Returns:
|
| 145 |
+
True if handshake is ready
|
| 146 |
+
"""
|
| 147 |
+
peer_key = f"{peer.organization_id}/{peer.agent_id}"
|
| 148 |
+
manager = self.handshake_managers.get(peer_key)
|
| 149 |
+
return manager.is_ready() if manager else False
|
| 150 |
+
|
| 151 |
+
def get_peer_identity(self, peer: AgentReference) -> Optional[AgentIdentity]:
|
| 152 |
+
"""
|
| 153 |
+
Get peer's identity after handshake
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
peer: Peer agent reference
|
| 157 |
+
|
| 158 |
+
Returns:
|
| 159 |
+
Peer's identity if handshake completed
|
| 160 |
+
"""
|
| 161 |
+
peer_key = f"{peer.organization_id}/{peer.agent_id}"
|
| 162 |
+
manager = self.handshake_managers.get(peer_key)
|
| 163 |
+
return manager.peer_identity if manager else None
|
| 164 |
+
|
| 165 |
+
def register_handler(
|
| 166 |
+
self, message_type: MessageType, handler: Callable[[MessageEnvelope], Optional[MessageEnvelope]]
|
| 167 |
+
) -> None:
|
| 168 |
+
"""
|
| 169 |
+
Register custom message handler
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
message_type: Message type to handle
|
| 173 |
+
handler: Handler function
|
| 174 |
+
"""
|
| 175 |
+
self.message_handlers[message_type] = handler
|
| 176 |
+
|
| 177 |
+
def create_task_request(
|
| 178 |
+
self, peer: AgentReference, task_description: str, **kwargs
|
| 179 |
+
) -> MessageEnvelope:
|
| 180 |
+
"""
|
| 181 |
+
Create a task request message
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
peer: Target agent
|
| 185 |
+
task_description: Task description
|
| 186 |
+
**kwargs: Additional task parameters
|
| 187 |
+
|
| 188 |
+
Returns:
|
| 189 |
+
Task request message
|
| 190 |
+
"""
|
| 191 |
+
if not self.is_ready(peer):
|
| 192 |
+
raise HandshakeError("Handshake not complete with peer")
|
| 193 |
+
|
| 194 |
+
payload = {"description": task_description, **kwargs}
|
| 195 |
+
|
| 196 |
+
return MessageEnvelope(
|
| 197 |
+
type=MessageType.TASK_REQUEST,
|
| 198 |
+
sender=self.identity.to_reference(),
|
| 199 |
+
receiver=peer,
|
| 200 |
+
payload=payload,
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
def __repr__(self) -> str:
|
| 204 |
+
return (
|
| 205 |
+
f"AIPMAgent(agent_id={self.identity.agent_id}, "
|
| 206 |
+
f"org={self.identity.organization_id}, "
|
| 207 |
+
f"name={self.identity.name})"
|
| 208 |
+
)
|
sdk-python/aipm/crypto.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Cryptographic utilities for AIPM
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import hashlib
|
| 6 |
+
import base64
|
| 7 |
+
from typing import Optional, Tuple
|
| 8 |
+
|
| 9 |
+
from cryptography.hazmat.primitives import hashes, serialization
|
| 10 |
+
from cryptography.hazmat.primitives.asymmetric import ed25519
|
| 11 |
+
from cryptography.hazmat.backends import default_backend
|
| 12 |
+
|
| 13 |
+
from aipm.exceptions import EncryptionError, AuthenticationError
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class CryptoManager:
|
| 17 |
+
"""Manages cryptographic operations for AIPM"""
|
| 18 |
+
|
| 19 |
+
def __init__(self):
|
| 20 |
+
self.private_key: Optional[ed25519.Ed25519PrivateKey] = None
|
| 21 |
+
self.public_key: Optional[ed25519.Ed25519PublicKey] = None
|
| 22 |
+
|
| 23 |
+
def generate_keypair(self) -> Tuple[str, str]:
|
| 24 |
+
"""
|
| 25 |
+
Generate Ed25519 keypair
|
| 26 |
+
|
| 27 |
+
Returns:
|
| 28 |
+
Tuple of (private_key_b64, public_key_b64)
|
| 29 |
+
"""
|
| 30 |
+
self.private_key = ed25519.Ed25519PrivateKey.generate()
|
| 31 |
+
self.public_key = self.private_key.public_key()
|
| 32 |
+
|
| 33 |
+
# Serialize to bytes
|
| 34 |
+
private_bytes = self.private_key.private_bytes(
|
| 35 |
+
encoding=serialization.Encoding.Raw,
|
| 36 |
+
format=serialization.PrivateFormat.Raw,
|
| 37 |
+
encryption_algorithm=serialization.NoEncryption(),
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
public_bytes = self.public_key.public_bytes(
|
| 41 |
+
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
# Encode to base64
|
| 45 |
+
private_b64 = base64.b64encode(private_bytes).decode("utf-8")
|
| 46 |
+
public_b64 = base64.b64encode(public_bytes).decode("utf-8")
|
| 47 |
+
|
| 48 |
+
return private_b64, public_b64
|
| 49 |
+
|
| 50 |
+
def load_private_key(self, private_key_b64: str) -> None:
|
| 51 |
+
"""Load private key from base64 string"""
|
| 52 |
+
try:
|
| 53 |
+
private_bytes = base64.b64decode(private_key_b64)
|
| 54 |
+
self.private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_bytes)
|
| 55 |
+
self.public_key = self.private_key.public_key()
|
| 56 |
+
except Exception as e:
|
| 57 |
+
raise EncryptionError(f"Failed to load private key: {e}")
|
| 58 |
+
|
| 59 |
+
def load_public_key(self, public_key_b64: str) -> ed25519.Ed25519PublicKey:
|
| 60 |
+
"""Load public key from base64 string"""
|
| 61 |
+
try:
|
| 62 |
+
public_bytes = base64.b64decode(public_key_b64)
|
| 63 |
+
return ed25519.Ed25519PublicKey.from_public_bytes(public_bytes)
|
| 64 |
+
except Exception as e:
|
| 65 |
+
raise EncryptionError(f"Failed to load public key: {e}")
|
| 66 |
+
|
| 67 |
+
def sign_message(self, message: str) -> str:
|
| 68 |
+
"""
|
| 69 |
+
Sign a message with private key
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
message: Message string to sign
|
| 73 |
+
|
| 74 |
+
Returns:
|
| 75 |
+
Base64-encoded signature
|
| 76 |
+
"""
|
| 77 |
+
if not self.private_key:
|
| 78 |
+
raise EncryptionError("Private key not loaded")
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
message_bytes = message.encode("utf-8")
|
| 82 |
+
signature = self.private_key.sign(message_bytes)
|
| 83 |
+
return base64.b64encode(signature).decode("utf-8")
|
| 84 |
+
except Exception as e:
|
| 85 |
+
raise EncryptionError(f"Failed to sign message: {e}")
|
| 86 |
+
|
| 87 |
+
def verify_signature(
|
| 88 |
+
self, message: str, signature_b64: str, public_key_b64: str
|
| 89 |
+
) -> bool:
|
| 90 |
+
"""
|
| 91 |
+
Verify message signature
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
message: Original message
|
| 95 |
+
signature_b64: Base64-encoded signature
|
| 96 |
+
public_key_b64: Base64-encoded public key
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
True if signature is valid
|
| 100 |
+
"""
|
| 101 |
+
try:
|
| 102 |
+
public_key = self.load_public_key(public_key_b64)
|
| 103 |
+
message_bytes = message.encode("utf-8")
|
| 104 |
+
signature = base64.b64decode(signature_b64)
|
| 105 |
+
|
| 106 |
+
public_key.verify(signature, message_bytes)
|
| 107 |
+
return True
|
| 108 |
+
except Exception:
|
| 109 |
+
return False
|
| 110 |
+
|
| 111 |
+
def get_public_key_fingerprint(self, public_key_b64: str) -> str:
|
| 112 |
+
"""
|
| 113 |
+
Generate SHA256 fingerprint of public key
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
public_key_b64: Base64-encoded public key
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
Hex-encoded SHA256 fingerprint
|
| 120 |
+
"""
|
| 121 |
+
public_bytes = base64.b64decode(public_key_b64)
|
| 122 |
+
fingerprint = hashlib.sha256(public_bytes).hexdigest()
|
| 123 |
+
return fingerprint
|
| 124 |
+
|
| 125 |
+
def get_own_fingerprint(self) -> str:
|
| 126 |
+
"""Get fingerprint of own public key"""
|
| 127 |
+
if not self.public_key:
|
| 128 |
+
raise EncryptionError("Public key not loaded")
|
| 129 |
+
|
| 130 |
+
public_bytes = self.public_key.public_bytes(
|
| 131 |
+
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
|
| 132 |
+
)
|
| 133 |
+
return hashlib.sha256(public_bytes).hexdigest()
|
sdk-python/aipm/exceptions.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
AIPM exception classes
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class AIPMException(Exception):
|
| 7 |
+
"""Base exception for AIPM"""
|
| 8 |
+
|
| 9 |
+
pass
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class HandshakeError(AIPMException):
|
| 13 |
+
"""Handshake protocol error"""
|
| 14 |
+
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ValidationError(AIPMException):
|
| 19 |
+
"""Message validation error"""
|
| 20 |
+
|
| 21 |
+
pass
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class AuthenticationError(AIPMException):
|
| 25 |
+
"""Authentication failure"""
|
| 26 |
+
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class EncryptionError(AIPMException):
|
| 31 |
+
"""Encryption/decryption error"""
|
| 32 |
+
|
| 33 |
+
pass
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class TrustError(AIPMException):
|
| 37 |
+
"""Trust verification error"""
|
| 38 |
+
|
| 39 |
+
pass
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class CapabilityError(AIPMException):
|
| 43 |
+
"""Capability negotiation error"""
|
| 44 |
+
|
| 45 |
+
pass
|
sdk-python/aipm/handshake.py
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Handshake protocol implementation for AIPM
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from enum import Enum
|
| 6 |
+
from typing import Optional
|
| 7 |
+
from uuid import uuid4
|
| 8 |
+
|
| 9 |
+
from aipm.models import (
|
| 10 |
+
AgentIdentity,
|
| 11 |
+
AgentReference,
|
| 12 |
+
MessageEnvelope,
|
| 13 |
+
MessageType,
|
| 14 |
+
HandshakePayload,
|
| 15 |
+
)
|
| 16 |
+
from aipm.exceptions import HandshakeError
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class HandshakeState(str, Enum):
|
| 20 |
+
"""Handshake state machine states"""
|
| 21 |
+
|
| 22 |
+
IDLE = "idle"
|
| 23 |
+
HELLO_SENT = "hello_sent"
|
| 24 |
+
HELLO_RECEIVED = "hello_received"
|
| 25 |
+
CAPABILITY_EXCHANGED = "capability_exchanged"
|
| 26 |
+
AUTHENTICATED = "authenticated"
|
| 27 |
+
KEY_EXCHANGED = "key_exchanged"
|
| 28 |
+
TRUST_VERIFIED = "trust_verified"
|
| 29 |
+
READY = "ready"
|
| 30 |
+
FAILED = "failed"
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class HandshakeManager:
|
| 34 |
+
"""Manages the AIPM handshake protocol state machine"""
|
| 35 |
+
|
| 36 |
+
def __init__(self, identity: AgentIdentity):
|
| 37 |
+
"""
|
| 38 |
+
Initialize handshake manager
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
identity: This agent's identity
|
| 42 |
+
"""
|
| 43 |
+
self.identity = identity
|
| 44 |
+
self.state = HandshakeState.IDLE
|
| 45 |
+
self.session_id: Optional[str] = None
|
| 46 |
+
self.peer_identity: Optional[AgentIdentity] = None
|
| 47 |
+
self.challenge: Optional[str] = None
|
| 48 |
+
|
| 49 |
+
def create_hello_message(self, receiver: AgentReference) -> MessageEnvelope:
|
| 50 |
+
"""
|
| 51 |
+
Create initial HELLO message to start handshake
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
receiver: Target agent reference
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
MessageEnvelope with HELLO type
|
| 58 |
+
"""
|
| 59 |
+
if self.state != HandshakeState.IDLE:
|
| 60 |
+
raise HandshakeError(f"Cannot send HELLO from state {self.state}")
|
| 61 |
+
|
| 62 |
+
self.session_id = str(uuid4())
|
| 63 |
+
self.state = HandshakeState.HELLO_SENT
|
| 64 |
+
|
| 65 |
+
payload = HandshakePayload(
|
| 66 |
+
identity=self.identity, session_id=self.session_id, status="initiating"
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
return MessageEnvelope(
|
| 70 |
+
type=MessageType.HANDSHAKE_HELLO,
|
| 71 |
+
sender=self.identity.to_reference(),
|
| 72 |
+
receiver=receiver,
|
| 73 |
+
payload=payload.model_dump(exclude_none=True),
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
def handle_hello_message(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 77 |
+
"""
|
| 78 |
+
Handle incoming HELLO message and respond with capability exchange
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
message: Incoming HELLO message
|
| 82 |
+
|
| 83 |
+
Returns:
|
| 84 |
+
Response message with capabilities
|
| 85 |
+
"""
|
| 86 |
+
if self.state != HandshakeState.IDLE:
|
| 87 |
+
raise HandshakeError(f"Cannot receive HELLO in state {self.state}")
|
| 88 |
+
|
| 89 |
+
# Parse payload
|
| 90 |
+
payload = HandshakePayload(**message.payload)
|
| 91 |
+
if not payload.identity or not payload.session_id:
|
| 92 |
+
raise HandshakeError("Invalid HELLO payload")
|
| 93 |
+
|
| 94 |
+
self.peer_identity = payload.identity
|
| 95 |
+
self.session_id = payload.session_id
|
| 96 |
+
self.state = HandshakeState.HELLO_RECEIVED
|
| 97 |
+
|
| 98 |
+
# Respond with capability exchange
|
| 99 |
+
return self.create_capability_exchange_message(message.sender)
|
| 100 |
+
|
| 101 |
+
def create_capability_exchange_message(
|
| 102 |
+
self, receiver: AgentReference
|
| 103 |
+
) -> MessageEnvelope:
|
| 104 |
+
"""
|
| 105 |
+
Create capability exchange message
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
receiver: Target agent
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
MessageEnvelope with capabilities
|
| 112 |
+
"""
|
| 113 |
+
if self.state not in [
|
| 114 |
+
HandshakeState.HELLO_SENT,
|
| 115 |
+
HandshakeState.HELLO_RECEIVED,
|
| 116 |
+
]:
|
| 117 |
+
raise HandshakeError(
|
| 118 |
+
f"Cannot exchange capabilities from state {self.state}"
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
self.state = HandshakeState.CAPABILITY_EXCHANGED
|
| 122 |
+
|
| 123 |
+
payload = HandshakePayload(
|
| 124 |
+
identity=self.identity,
|
| 125 |
+
session_id=self.session_id,
|
| 126 |
+
status="capabilities_shared",
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
return MessageEnvelope(
|
| 130 |
+
type=MessageType.HANDSHAKE_CAPABILITY_EXCHANGE,
|
| 131 |
+
sender=self.identity.to_reference(),
|
| 132 |
+
receiver=receiver,
|
| 133 |
+
payload=payload.model_dump(exclude_none=True),
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
def handle_capability_exchange(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 137 |
+
"""
|
| 138 |
+
Handle capability exchange and create authentication message
|
| 139 |
+
|
| 140 |
+
Args:
|
| 141 |
+
message: Incoming capability exchange message
|
| 142 |
+
|
| 143 |
+
Returns:
|
| 144 |
+
Authentication message
|
| 145 |
+
"""
|
| 146 |
+
if self.state not in [
|
| 147 |
+
HandshakeState.HELLO_SENT,
|
| 148 |
+
HandshakeState.CAPABILITY_EXCHANGED,
|
| 149 |
+
]:
|
| 150 |
+
raise HandshakeError(
|
| 151 |
+
f"Cannot handle capability exchange in state {self.state}"
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
payload = HandshakePayload(**message.payload)
|
| 155 |
+
if not payload.identity:
|
| 156 |
+
raise HandshakeError("Invalid capability exchange payload")
|
| 157 |
+
|
| 158 |
+
self.peer_identity = payload.identity
|
| 159 |
+
self.state = HandshakeState.CAPABILITY_EXCHANGED
|
| 160 |
+
|
| 161 |
+
# Move to authentication
|
| 162 |
+
return self.create_authentication_message(message.sender)
|
| 163 |
+
|
| 164 |
+
def create_authentication_message(self, receiver: AgentReference) -> MessageEnvelope:
|
| 165 |
+
"""
|
| 166 |
+
Create authentication challenge/response
|
| 167 |
+
|
| 168 |
+
Args:
|
| 169 |
+
receiver: Target agent
|
| 170 |
+
|
| 171 |
+
Returns:
|
| 172 |
+
Authentication message
|
| 173 |
+
"""
|
| 174 |
+
if self.state != HandshakeState.CAPABILITY_EXCHANGED:
|
| 175 |
+
raise HandshakeError(f"Cannot authenticate from state {self.state}")
|
| 176 |
+
|
| 177 |
+
# Generate challenge
|
| 178 |
+
self.challenge = str(uuid4())
|
| 179 |
+
self.state = HandshakeState.AUTHENTICATED
|
| 180 |
+
|
| 181 |
+
payload = HandshakePayload(
|
| 182 |
+
session_id=self.session_id, challenge=self.challenge, status="authenticated"
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
return MessageEnvelope(
|
| 186 |
+
type=MessageType.HANDSHAKE_AUTHENTICATION,
|
| 187 |
+
sender=self.identity.to_reference(),
|
| 188 |
+
receiver=receiver,
|
| 189 |
+
payload=payload.model_dump(exclude_none=True),
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
def handle_authentication(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 193 |
+
"""
|
| 194 |
+
Handle authentication and exchange public keys
|
| 195 |
+
|
| 196 |
+
Args:
|
| 197 |
+
message: Authentication message
|
| 198 |
+
|
| 199 |
+
Returns:
|
| 200 |
+
Public key exchange message
|
| 201 |
+
"""
|
| 202 |
+
if self.state != HandshakeState.CAPABILITY_EXCHANGED:
|
| 203 |
+
raise HandshakeError(f"Cannot handle authentication in state {self.state}")
|
| 204 |
+
|
| 205 |
+
payload = HandshakePayload(**message.payload)
|
| 206 |
+
self.state = HandshakeState.AUTHENTICATED
|
| 207 |
+
|
| 208 |
+
return self.create_key_exchange_message(message.sender)
|
| 209 |
+
|
| 210 |
+
def create_key_exchange_message(self, receiver: AgentReference) -> MessageEnvelope:
|
| 211 |
+
"""
|
| 212 |
+
Create public key exchange message
|
| 213 |
+
|
| 214 |
+
Args:
|
| 215 |
+
receiver: Target agent
|
| 216 |
+
|
| 217 |
+
Returns:
|
| 218 |
+
Key exchange message
|
| 219 |
+
"""
|
| 220 |
+
if self.state != HandshakeState.AUTHENTICATED:
|
| 221 |
+
raise HandshakeError(f"Cannot exchange keys from state {self.state}")
|
| 222 |
+
|
| 223 |
+
self.state = HandshakeState.KEY_EXCHANGED
|
| 224 |
+
|
| 225 |
+
payload = HandshakePayload(
|
| 226 |
+
session_id=self.session_id,
|
| 227 |
+
identity=self.identity,
|
| 228 |
+
status="keys_exchanged",
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
return MessageEnvelope(
|
| 232 |
+
type=MessageType.HANDSHAKE_PUBLIC_KEY_EXCHANGE,
|
| 233 |
+
sender=self.identity.to_reference(),
|
| 234 |
+
receiver=receiver,
|
| 235 |
+
payload=payload.model_dump(exclude_none=True),
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
def handle_key_exchange(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 239 |
+
"""
|
| 240 |
+
Handle key exchange and verify trust
|
| 241 |
+
|
| 242 |
+
Args:
|
| 243 |
+
message: Key exchange message
|
| 244 |
+
|
| 245 |
+
Returns:
|
| 246 |
+
Trust verification message
|
| 247 |
+
"""
|
| 248 |
+
if self.state != HandshakeState.AUTHENTICATED:
|
| 249 |
+
raise HandshakeError(f"Cannot handle key exchange in state {self.state}")
|
| 250 |
+
|
| 251 |
+
payload = HandshakePayload(**message.payload)
|
| 252 |
+
if payload.identity and payload.identity.public_key:
|
| 253 |
+
self.peer_identity = payload.identity
|
| 254 |
+
|
| 255 |
+
self.state = HandshakeState.KEY_EXCHANGED
|
| 256 |
+
|
| 257 |
+
return self.create_trust_verification_message(message.sender)
|
| 258 |
+
|
| 259 |
+
def create_trust_verification_message(
|
| 260 |
+
self, receiver: AgentReference
|
| 261 |
+
) -> MessageEnvelope:
|
| 262 |
+
"""
|
| 263 |
+
Create trust verification message
|
| 264 |
+
|
| 265 |
+
Args:
|
| 266 |
+
receiver: Target agent
|
| 267 |
+
|
| 268 |
+
Returns:
|
| 269 |
+
Trust verification message
|
| 270 |
+
"""
|
| 271 |
+
if self.state != HandshakeState.KEY_EXCHANGED:
|
| 272 |
+
raise HandshakeError(f"Cannot verify trust from state {self.state}")
|
| 273 |
+
|
| 274 |
+
self.state = HandshakeState.TRUST_VERIFIED
|
| 275 |
+
|
| 276 |
+
payload = HandshakePayload(
|
| 277 |
+
session_id=self.session_id,
|
| 278 |
+
identity=self.identity,
|
| 279 |
+
status="trust_verified",
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
return MessageEnvelope(
|
| 283 |
+
type=MessageType.HANDSHAKE_TRUST_VERIFICATION,
|
| 284 |
+
sender=self.identity.to_reference(),
|
| 285 |
+
receiver=receiver,
|
| 286 |
+
payload=payload.model_dump(exclude_none=True),
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
def handle_trust_verification(self, message: MessageEnvelope) -> MessageEnvelope:
|
| 290 |
+
"""
|
| 291 |
+
Handle trust verification and finalize handshake
|
| 292 |
+
|
| 293 |
+
Args:
|
| 294 |
+
message: Trust verification message
|
| 295 |
+
|
| 296 |
+
Returns:
|
| 297 |
+
READY message
|
| 298 |
+
"""
|
| 299 |
+
if self.state != HandshakeState.KEY_EXCHANGED:
|
| 300 |
+
raise HandshakeError(
|
| 301 |
+
f"Cannot handle trust verification in state {self.state}"
|
| 302 |
+
)
|
| 303 |
+
|
| 304 |
+
self.state = HandshakeState.TRUST_VERIFIED
|
| 305 |
+
|
| 306 |
+
return self.create_ready_message(message.sender)
|
| 307 |
+
|
| 308 |
+
def create_ready_message(self, receiver: AgentReference) -> MessageEnvelope:
|
| 309 |
+
"""
|
| 310 |
+
Create READY message to finalize handshake
|
| 311 |
+
|
| 312 |
+
Args:
|
| 313 |
+
receiver: Target agent
|
| 314 |
+
|
| 315 |
+
Returns:
|
| 316 |
+
READY message
|
| 317 |
+
"""
|
| 318 |
+
if self.state != HandshakeState.TRUST_VERIFIED:
|
| 319 |
+
raise HandshakeError(f"Cannot send READY from state {self.state}")
|
| 320 |
+
|
| 321 |
+
self.state = HandshakeState.READY
|
| 322 |
+
|
| 323 |
+
payload = HandshakePayload(session_id=self.session_id, status="ready")
|
| 324 |
+
|
| 325 |
+
return MessageEnvelope(
|
| 326 |
+
type=MessageType.HANDSHAKE_READY,
|
| 327 |
+
sender=self.identity.to_reference(),
|
| 328 |
+
receiver=receiver,
|
| 329 |
+
payload=payload.model_dump(exclude_none=True),
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
+
def handle_ready_message(self, message: MessageEnvelope) -> None:
|
| 333 |
+
"""
|
| 334 |
+
Handle READY message and complete handshake
|
| 335 |
+
|
| 336 |
+
Args:
|
| 337 |
+
message: READY message
|
| 338 |
+
"""
|
| 339 |
+
if self.state != HandshakeState.TRUST_VERIFIED:
|
| 340 |
+
raise HandshakeError(f"Cannot handle READY in state {self.state}")
|
| 341 |
+
|
| 342 |
+
self.state = HandshakeState.READY
|
| 343 |
+
|
| 344 |
+
def is_ready(self) -> bool:
|
| 345 |
+
"""Check if handshake is complete and ready for communication"""
|
| 346 |
+
return self.state == HandshakeState.READY
|
| 347 |
+
|
| 348 |
+
def reset(self) -> None:
|
| 349 |
+
"""Reset handshake state to IDLE"""
|
| 350 |
+
self.state = HandshakeState.IDLE
|
| 351 |
+
self.session_id = None
|
| 352 |
+
self.peer_identity = None
|
| 353 |
+
self.challenge = None
|
sdk-python/aipm/models.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Core data models for AIPM protocol
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from enum import Enum
|
| 7 |
+
from typing import Any, Dict, List, Optional
|
| 8 |
+
from uuid import uuid4
|
| 9 |
+
|
| 10 |
+
from pydantic import BaseModel, Field, field_validator
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class MessageType(str, Enum):
|
| 14 |
+
"""Message type enumeration"""
|
| 15 |
+
|
| 16 |
+
HANDSHAKE_HELLO = "handshake.hello"
|
| 17 |
+
HANDSHAKE_CAPABILITY_EXCHANGE = "handshake.capability_exchange"
|
| 18 |
+
HANDSHAKE_AUTHENTICATION = "handshake.authentication"
|
| 19 |
+
HANDSHAKE_PUBLIC_KEY_EXCHANGE = "handshake.public_key_exchange"
|
| 20 |
+
HANDSHAKE_TRUST_VERIFICATION = "handshake.trust_verification"
|
| 21 |
+
HANDSHAKE_READY = "handshake.ready"
|
| 22 |
+
TASK_REQUEST = "task.request"
|
| 23 |
+
TASK_ACCEPT = "task.accept"
|
| 24 |
+
TASK_DECLINE = "task.decline"
|
| 25 |
+
TASK_PROGRESS = "task.progress"
|
| 26 |
+
TASK_COMPLETE = "task.complete"
|
| 27 |
+
TASK_ERROR = "task.error"
|
| 28 |
+
MEMORY_SHARE = "memory.share"
|
| 29 |
+
MEMORY_REQUEST = "memory.request"
|
| 30 |
+
CAPABILITY_QUERY = "capability.query"
|
| 31 |
+
CAPABILITY_RESPONSE = "capability.response"
|
| 32 |
+
TRUST_UPDATE = "trust.update"
|
| 33 |
+
ERROR = "error"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class Priority(str, Enum):
|
| 37 |
+
"""Message priority levels"""
|
| 38 |
+
|
| 39 |
+
LOW = "low"
|
| 40 |
+
NORMAL = "normal"
|
| 41 |
+
HIGH = "high"
|
| 42 |
+
CRITICAL = "critical"
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class AgentReference(BaseModel):
|
| 46 |
+
"""Minimal agent reference for message routing"""
|
| 47 |
+
|
| 48 |
+
agent_id: str = Field(..., description="Unique agent identifier")
|
| 49 |
+
organization_id: str = Field(..., description="Organization or vendor identifier")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class Capabilities(BaseModel):
|
| 53 |
+
"""Agent capabilities declaration"""
|
| 54 |
+
|
| 55 |
+
skills: List[str] = Field(default_factory=list, description="Supported skills")
|
| 56 |
+
models: List[str] = Field(default_factory=list, description="AI models available")
|
| 57 |
+
tools: List[str] = Field(default_factory=list, description="Available tools and APIs")
|
| 58 |
+
max_context: Optional[int] = Field(None, description="Maximum context window size")
|
| 59 |
+
memory_support: bool = Field(False, description="Supports memory exchange")
|
| 60 |
+
languages: List[str] = Field(
|
| 61 |
+
default_factory=lambda: ["en"], description="Supported languages"
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class TrustScore(BaseModel):
|
| 66 |
+
"""Trust and reputation metrics"""
|
| 67 |
+
|
| 68 |
+
reliability: float = Field(1.0, ge=0.0, le=1.0, description="Reliability score")
|
| 69 |
+
accuracy: float = Field(1.0, ge=0.0, le=1.0, description="Accuracy score")
|
| 70 |
+
avg_latency_ms: float = Field(0.0, ge=0.0, description="Average latency in milliseconds")
|
| 71 |
+
success_rate: float = Field(1.0, ge=0.0, le=1.0, description="Success rate")
|
| 72 |
+
total_interactions: int = Field(0, ge=0, description="Total number of interactions")
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class EconomicInfo(BaseModel):
|
| 76 |
+
"""Economic and billing information"""
|
| 77 |
+
|
| 78 |
+
cost_per_request: Optional[float] = Field(None, description="Cost per request")
|
| 79 |
+
currency: str = Field("USD", description="Currency code")
|
| 80 |
+
billing_model: str = Field(
|
| 81 |
+
"per_request",
|
| 82 |
+
description="Billing model",
|
| 83 |
+
pattern="^(per_request|per_token|subscription)$",
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class AgentIdentity(BaseModel):
|
| 88 |
+
"""Complete agent identity with capabilities"""
|
| 89 |
+
|
| 90 |
+
agent_id: str = Field(..., description="Unique agent identifier")
|
| 91 |
+
organization_id: str = Field(..., description="Organization or vendor identifier")
|
| 92 |
+
name: str = Field(..., description="Human-readable agent name")
|
| 93 |
+
version: str = Field(..., description="Agent version")
|
| 94 |
+
public_key: Optional[str] = Field(None, description="Base64-encoded public key")
|
| 95 |
+
capabilities: Capabilities = Field(default_factory=Capabilities)
|
| 96 |
+
trust_score: TrustScore = Field(default_factory=TrustScore)
|
| 97 |
+
economic: Optional[EconomicInfo] = Field(None, description="Economic information")
|
| 98 |
+
|
| 99 |
+
def to_reference(self) -> AgentReference:
|
| 100 |
+
"""Convert to minimal agent reference"""
|
| 101 |
+
return AgentReference(agent_id=self.agent_id, organization_id=self.organization_id)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class SignatureInfo(BaseModel):
|
| 105 |
+
"""Cryptographic signature information"""
|
| 106 |
+
|
| 107 |
+
algorithm: str = Field(..., description="Signature algorithm")
|
| 108 |
+
value: str = Field(..., description="Base64-encoded signature")
|
| 109 |
+
public_key_fingerprint: str = Field(..., description="SHA256 fingerprint of public key")
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
class EncryptionInfo(BaseModel):
|
| 113 |
+
"""Encryption metadata"""
|
| 114 |
+
|
| 115 |
+
algorithm: str = Field(..., description="Encryption algorithm")
|
| 116 |
+
encrypted: bool = Field(False, description="Whether payload is encrypted")
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class MessageEnvelope(BaseModel):
|
| 120 |
+
"""Standard AIPM message envelope"""
|
| 121 |
+
|
| 122 |
+
id: str = Field(default_factory=lambda: str(uuid4()), description="Unique message ID")
|
| 123 |
+
type: MessageType = Field(..., description="Message type")
|
| 124 |
+
version: str = Field("1.0.0", description="Protocol version")
|
| 125 |
+
sender: AgentReference = Field(..., description="Sender agent")
|
| 126 |
+
receiver: AgentReference = Field(..., description="Receiver agent")
|
| 127 |
+
timestamp: datetime = Field(default_factory=datetime.utcnow, description="Message timestamp")
|
| 128 |
+
priority: Priority = Field(Priority.NORMAL, description="Message priority")
|
| 129 |
+
deadline: Optional[datetime] = Field(None, description="Optional response deadline")
|
| 130 |
+
correlation_id: Optional[str] = Field(None, description="Correlated message ID")
|
| 131 |
+
payload: Dict[str, Any] = Field(default_factory=dict, description="Message payload")
|
| 132 |
+
signature: Optional[SignatureInfo] = Field(None, description="Message signature")
|
| 133 |
+
encryption: Optional[EncryptionInfo] = Field(None, description="Encryption metadata")
|
| 134 |
+
|
| 135 |
+
@field_validator("version")
|
| 136 |
+
@classmethod
|
| 137 |
+
def validate_version(cls, v: str) -> str:
|
| 138 |
+
"""Validate version format (semver)"""
|
| 139 |
+
parts = v.split(".")
|
| 140 |
+
if len(parts) != 3:
|
| 141 |
+
raise ValueError("Version must be in format X.Y.Z")
|
| 142 |
+
for part in parts:
|
| 143 |
+
if not part.isdigit():
|
| 144 |
+
raise ValueError("Version parts must be numeric")
|
| 145 |
+
return v
|
| 146 |
+
|
| 147 |
+
def model_dump_json(self, **kwargs) -> str:
|
| 148 |
+
"""Serialize to JSON with ISO timestamps"""
|
| 149 |
+
return super().model_dump_json(exclude_none=True, **kwargs)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
class HandshakePayload(BaseModel):
|
| 153 |
+
"""Payload for handshake messages"""
|
| 154 |
+
|
| 155 |
+
identity: Optional[AgentIdentity] = None
|
| 156 |
+
session_id: Optional[str] = None
|
| 157 |
+
challenge: Optional[str] = None
|
| 158 |
+
response: Optional[str] = None
|
| 159 |
+
status: Optional[str] = None
|
| 160 |
+
error: Optional[str] = None
|
sdk-python/pyproject.toml
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=68.0", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "aipm"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "Agent Interoperability Protocol Models - Python SDK"
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
requires-python = ">=3.9"
|
| 11 |
+
license = {text = "Apache-2.0"}
|
| 12 |
+
authors = [
|
| 13 |
+
{name = "AIPM Contributors"}
|
| 14 |
+
]
|
| 15 |
+
keywords = ["ai", "agents", "interoperability", "protocol", "multi-agent"]
|
| 16 |
+
classifiers = [
|
| 17 |
+
"Development Status :: 3 - Alpha",
|
| 18 |
+
"Intended Audience :: Developers",
|
| 19 |
+
"License :: OSI Approved :: Apache Software License",
|
| 20 |
+
"Programming Language :: Python :: 3",
|
| 21 |
+
"Programming Language :: Python :: 3.9",
|
| 22 |
+
"Programming Language :: Python :: 3.10",
|
| 23 |
+
"Programming Language :: Python :: 3.11",
|
| 24 |
+
"Programming Language :: Python :: 3.12",
|
| 25 |
+
]
|
| 26 |
+
dependencies = [
|
| 27 |
+
"pydantic>=2.0.0",
|
| 28 |
+
"cryptography>=41.0.0",
|
| 29 |
+
"httpx>=0.25.0",
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
[project.optional-dependencies]
|
| 33 |
+
dev = [
|
| 34 |
+
"pytest>=7.0.0",
|
| 35 |
+
"pytest-asyncio>=0.21.0",
|
| 36 |
+
"black>=23.0.0",
|
| 37 |
+
"ruff>=0.1.0",
|
| 38 |
+
"mypy>=1.7.0",
|
| 39 |
+
]
|
| 40 |
+
|
| 41 |
+
[project.urls]
|
| 42 |
+
Homepage = "https://github.com/aipm/aipm"
|
| 43 |
+
Documentation = "https://docs.aipm.org"
|
| 44 |
+
Repository = "https://github.com/aipm/aipm"
|
| 45 |
+
|
| 46 |
+
[tool.setuptools]
|
| 47 |
+
packages = ["aipm"]
|
| 48 |
+
|
| 49 |
+
[tool.black]
|
| 50 |
+
line-length = 100
|
| 51 |
+
target-version = ["py39"]
|
| 52 |
+
|
| 53 |
+
[tool.ruff]
|
| 54 |
+
line-length = 100
|
| 55 |
+
target-version = "py39"
|
| 56 |
+
|
| 57 |
+
[tool.mypy]
|
| 58 |
+
python_version = "3.9"
|
| 59 |
+
strict = true
|